2 * $RCSId: xc/lib/fontconfig/src/fccfg.c,v 1.23 2002/08/31 22:17:32 keithp Exp $
4 * Copyright © 2000 Keith Packard
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)
54 if (!FcConfigSetCache (config, (FcChar8 *) ("~/" FC_USER_CACHE_FILE)))
59 config->substPattern = 0;
60 config->substFont = 0;
61 config->maxObjects = 0;
62 for (set = FcSetSystem; set <= FcSetApplication; set++)
63 config->fonts[set] = 0;
65 config->rescanTime = time(0);
66 config->rescanInterval = 30;
71 FcStrSetDestroy (config->fontDirs);
73 FcStrSetDestroy (config->configFiles);
75 FcStrSetDestroy (config->configDirs);
78 FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
83 typedef struct _FcFileTime {
89 FcConfigNewestFile (FcStrSet *files)
91 FcStrList *list = FcStrListCreate (files);
92 FcFileTime newest = { 0, FcFalse };
98 while ((file = FcStrListNext (list)))
99 if (stat ((char *) file, &statb) == 0)
100 if (!newest.set || statb.st_mtime - newest.time > 0)
101 newest.time = statb.st_mtime;
102 FcStrListDone (list);
108 FcConfigUptoDate (FcConfig *config)
110 FcFileTime config_time, font_time;
111 time_t now = time(0);
114 config = FcConfigGetCurrent ();
118 config_time = FcConfigNewestFile (config->configFiles);
119 font_time = FcConfigNewestFile (config->configDirs);
120 if ((config_time.set && config_time.time - config->rescanTime > 0) ||
121 (font_time.set && font_time.time - config->rescanTime) > 0)
125 config->rescanTime = now;
130 FcSubstDestroy (FcSubst *s)
138 FcTestDestroy (s->test);
140 FcEditDestroy (s->edit);
146 FcConfigDestroy (FcConfig *config)
150 if (config == _fcConfig)
153 FcStrSetDestroy (config->configDirs);
154 FcStrSetDestroy (config->fontDirs);
155 FcStrSetDestroy (config->configFiles);
157 FcStrFree (config->cache);
159 FcSubstDestroy (config->substPattern);
160 FcSubstDestroy (config->substFont);
161 for (set = FcSetSystem; set <= FcSetApplication; set++)
162 if (config->fonts[set])
163 FcFontSetDestroy (config->fonts[set]);
165 FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
169 * Scan the current list of directories in the configuration
170 * and build the set of available fonts. Update the
171 * per-user cache file to reflect the new configuration
175 FcConfigBuildFonts (FcConfig *config)
178 FcGlobalCache *cache;
182 fonts = FcFontSetCreate ();
186 cache = FcGlobalCacheCreate ();
190 FcGlobalCacheLoad (cache, config->cache);
192 list = FcConfigGetFontDirs (config);
196 while ((dir = FcStrListNext (list)))
198 if (FcDebug () & FC_DBG_FONTSET)
199 printf ("scan dir %s\n", dir);
200 FcDirScan (fonts, config->fontDirs, cache, config->blanks, dir, FcFalse);
203 FcStrListDone (list);
205 if (FcDebug () & FC_DBG_FONTSET)
206 FcFontSetPrint (fonts);
208 FcGlobalCacheSave (cache, config->cache);
209 FcGlobalCacheDestroy (cache);
211 FcConfigSetFonts (config, fonts, FcSetSystem);
215 FcFontSetDestroy (fonts);
221 FcConfigSetCurrent (FcConfig *config)
224 if (!FcConfigBuildFonts (config))
228 FcConfigDestroy (_fcConfig);
234 FcConfigGetCurrent (void)
243 FcConfigAddConfigDir (FcConfig *config,
246 return FcStrSetAddFilename (config->configDirs, d);
250 FcConfigGetConfigDirs (FcConfig *config)
254 config = FcConfigGetCurrent ();
258 return FcStrListCreate (config->configDirs);
262 FcConfigAddFontDir (FcConfig *config,
265 return FcStrSetAddFilename (config->fontDirs, d);
269 FcConfigAddDir (FcConfig *config,
272 return (FcConfigAddConfigDir (config, d) &&
273 FcConfigAddFontDir (config, d));
277 FcConfigGetFontDirs (FcConfig *config)
281 config = FcConfigGetCurrent ();
285 return FcStrListCreate (config->fontDirs);
289 FcConfigAddConfigFile (FcConfig *config,
293 FcChar8 *file = FcConfigFilename (f);
298 ret = FcStrSetAdd (config->configFiles, file);
304 FcConfigGetConfigFiles (FcConfig *config)
308 config = FcConfigGetCurrent ();
312 return FcStrListCreate (config->configFiles);
316 FcConfigSetCache (FcConfig *config,
319 FcChar8 *new = FcStrCopyFilename (c);
324 FcStrFree (config->cache);
330 FcConfigGetCache (FcConfig *config)
334 config = FcConfigGetCurrent ();
338 return config->cache;
342 FcConfigGetFonts (FcConfig *config,
347 config = FcConfigGetCurrent ();
351 return config->fonts[set];
355 FcConfigSetFonts (FcConfig *config,
359 if (config->fonts[set])
360 FcFontSetDestroy (config->fonts[set]);
361 config->fonts[set] = fonts;
367 FcConfigGetBlanks (FcConfig *config)
371 config = FcConfigGetCurrent ();
375 return config->blanks;
379 FcConfigAddBlank (FcConfig *config,
387 b = FcBlanksCreate ();
391 if (!FcBlanksAdd (b, blank))
398 FcConfigGetRescanInverval (FcConfig *config)
402 config = FcConfigGetCurrent ();
406 return config->rescanInterval;
410 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
414 config = FcConfigGetCurrent ();
418 config->rescanInterval = rescanInterval;
423 FcConfigAddEdit (FcConfig *config,
428 FcSubst *subst, **prev;
432 subst = (FcSubst *) malloc (sizeof (FcSubst));
435 FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
436 if (kind == FcMatchPattern)
437 prev = &config->substPattern;
439 prev = &config->substFont;
440 for (; *prev; prev = &(*prev)->next);
446 for (t = test; t; t = t->next)
448 if (t->kind == FcMatchDefault)
452 if (config->maxObjects < num)
453 config->maxObjects = num;
454 if (FcDebug () & FC_DBG_EDIT)
456 printf ("Add Subst ");
457 FcSubstPrint (subst);
462 typedef struct _FcSubState {
468 FcConfigPromote (FcValue v, FcValue u)
470 if (v.type == FcTypeInteger)
472 v.type = FcTypeDouble;
473 v.u.d = (double) v.u.i;
475 else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
477 v.u.m = &FcIdentityMatrix;
478 v.type = FcTypeMatrix;
480 else if (v.type == FcTypeString && u.type == FcTypeLangSet)
482 v.u.l = FcLangSetPromote (v.u.s);
483 v.type = FcTypeLangSet;
489 FcConfigCompareValue (const FcValue m_o,
495 FcBool ret = FcFalse;
497 m = FcConfigPromote (m, v);
498 v = FcConfigPromote (v, m);
499 if (m.type == v.type)
503 break; /* FcConfigPromote prevents this from happening */
508 ret = m.u.d == v.u.d;
511 case FcOpNotContains:
512 ret = m.u.d != v.u.d;
518 ret = m.u.d <= v.u.d;
524 ret = m.u.d >= v.u.d;
534 ret = m.u.b == v.u.b;
537 case FcOpNotContains:
538 ret = m.u.b != v.u.b;
548 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
551 case FcOpNotContains:
552 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
562 ret = FcMatrixEqual (m.u.m, v.u.m);
565 case FcOpNotContains:
566 ret = !FcMatrixEqual (m.u.m, v.u.m);
575 /* m contains v if v is a subset of m */
576 ret = FcCharSetIsSubset (v.u.c, m.u.c);
578 case FcOpNotContains:
579 /* m contains v if v is a subset of m */
580 ret = !FcCharSetIsSubset (v.u.c, m.u.c);
583 ret = FcCharSetEqual (m.u.c, v.u.c);
586 ret = !FcCharSetEqual (m.u.c, v.u.c);
595 ret = FcLangSetContains (v.u.l, m.u.l);
597 case FcOpNotContains:
598 ret = FcLangSetContains (v.u.l, m.u.l);
601 ret = FcLangSetEqual (v.u.l, m.u.l);
604 ret = !FcLangSetEqual (v.u.l, m.u.l);
624 ret = m.u.f == v.u.f;
627 case FcOpNotContains:
628 ret = m.u.f != v.u.f;
638 if (op == FcOpNotEqual || op == FcOpNotContains)
646 FcConfigEvaluate (FcPattern *p, FcExpr *e)
654 v.type = FcTypeInteger;
658 v.type = FcTypeDouble;
662 v.type = FcTypeString;
667 v.type = FcTypeMatrix;
672 v.type = FcTypeCharSet;
681 r = FcPatternGet (p, e->u.field, 0, &v);
682 if (r != FcResultMatch)
686 if (FcNameConstant (e->u.constant, &v.u.i))
687 v.type = FcTypeInteger;
692 vl = FcConfigEvaluate (p, e->u.tree.left);
693 if (vl.type == FcTypeBool)
696 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
698 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
711 case FcOpNotContains:
712 vl = FcConfigEvaluate (p, e->u.tree.left);
713 vr = FcConfigEvaluate (p, e->u.tree.right);
715 v.u.b = FcConfigCompareValue (vl, e->op, vr);
725 vl = FcConfigEvaluate (p, e->u.tree.left);
726 vr = FcConfigEvaluate (p, e->u.tree.right);
727 vl = FcConfigPromote (vl, vr);
728 vr = FcConfigPromote (vr, vl);
729 if (vl.type == vr.type)
735 v.type = FcTypeDouble;
736 v.u.d = vl.u.d + vr.u.d;
739 v.type = FcTypeDouble;
740 v.u.d = vl.u.d - vr.u.d;
743 v.type = FcTypeDouble;
744 v.u.d = vl.u.d * vr.u.d;
747 v.type = FcTypeDouble;
748 v.u.d = vl.u.d / vr.u.d;
754 if (v.type == FcTypeDouble &&
755 v.u.d == (double) (int) v.u.d)
757 v.type = FcTypeInteger;
765 v.u.b = vl.u.b || vr.u.b;
769 v.u.b = vl.u.b && vr.u.b;
779 v.type = FcTypeString;
780 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
792 v.type = FcTypeMatrix;
793 m = malloc (sizeof (FcMatrix));
796 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
797 FcMatrixMultiply (m, vl.u.m, vr.u.m);
821 vl = FcConfigEvaluate (p, e->u.tree.left);
841 FcConfigMatchValueList (FcPattern *p,
845 FcValueList *ret = 0;
852 if (e->op == FcOpComma)
854 value = FcConfigEvaluate (p, e->u.tree.left);
859 value = FcConfigEvaluate (p, e);
863 for (v = values; v; v = v->next)
865 if (FcConfigCompareValue (v->value, t->op, value))
872 if (t->qual == FcQualAll)
879 FcValueDestroy (value);
885 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
891 l = (FcValueList *) malloc (sizeof (FcValueList));
894 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
895 if (e->op == FcOpComma)
897 l->value = FcConfigEvaluate (p, e->u.tree.left);
898 l->next = FcConfigValues (p, e->u.tree.right, binding);
902 l->value = FcConfigEvaluate (p, e);
905 l->binding = binding;
906 while (l && l->value.type == FcTypeVoid)
908 FcValueList *next = l->next;
910 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
918 FcConfigAdd (FcValueList **head,
919 FcValueList *position,
923 FcValueList **prev, *last, *v;
924 FcValueBinding sameBinding;
927 sameBinding = position->binding;
929 sameBinding = FcValueBindingWeak;
930 for (v = new; v; v = v->next)
931 if (v->binding == FcValueBindingSame)
932 v->binding = sameBinding;
936 prev = &position->next;
938 for (prev = head; *prev; prev = &(*prev)->next)
945 for (prev = head; *prev; prev = &(*prev)->next)
947 if (*prev == position)
954 if (FcDebug () & FC_DBG_EDIT)
957 printf ("position not on list\n");
961 if (FcDebug () & FC_DBG_EDIT)
963 printf ("%s list before ", append ? "Append" : "Prepend");
964 FcValueListPrint (*head);
978 if (FcDebug () & FC_DBG_EDIT)
980 printf ("%s list after ", append ? "Append" : "Prepend");
981 FcValueListPrint (*head);
989 FcConfigDel (FcValueList **head,
990 FcValueList *position)
994 for (prev = head; *prev; prev = &(*prev)->next)
996 if (*prev == position)
998 *prev = position->next;
1000 FcValueListDestroy (position);
1007 FcConfigPatternAdd (FcPattern *p,
1014 FcPatternElt *e = FcPatternInsertElt (p, object);
1018 FcConfigAdd (&e->values, 0, append, list);
1023 * Delete all values associated with a field
1026 FcConfigPatternDel (FcPattern *p,
1029 FcPatternElt *e = FcPatternFindElt (p, object);
1033 FcConfigDel (&e->values, e->values);
1037 FcConfigPatternCanon (FcPattern *p,
1040 FcPatternElt *e = FcPatternFindElt (p, object);
1044 FcPatternDel (p, object);
1048 FcConfigSubstituteWithPat (FcConfig *config,
1063 config = FcConfigGetCurrent ();
1068 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1069 if (!st && config->maxObjects)
1071 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1073 if (FcDebug () & FC_DBG_EDIT)
1075 printf ("FcConfigSubstitute ");
1078 if (kind == FcMatchPattern)
1079 s = config->substPattern;
1081 s = config->substFont;
1082 for (; s; s = s->next)
1085 * Check the tests to see if
1086 * they all match the pattern
1088 for (t = s->test, i = 0; t; t = t->next, i++)
1090 if (FcDebug () & FC_DBG_EDIT)
1092 printf ("FcConfigSubstitute test ");
1096 if (kind == FcMatchFont && t->kind == FcMatchPattern)
1101 st[i].elt = FcPatternFindElt (m, t->field);
1105 * If there's no such field in the font,
1106 * then FcQualAll matches while FcQualAny does not
1110 if (t->qual == FcQualAll)
1119 * Check to see if there is a match, mark the location
1120 * to apply match-relative edits
1122 st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1125 if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1127 if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1132 if (FcDebug () & FC_DBG_EDIT)
1133 printf ("No match\n");
1136 if (FcDebug () & FC_DBG_EDIT)
1138 printf ("Substitute ");
1141 for (e = s->edit; e; e = e->next)
1144 * Evaluate the list of expressions
1146 l = FcConfigValues (p, e->expr, e->binding);
1148 * Locate any test associated with this field, skipping
1149 * tests associated with the pattern when substituting in
1152 for (t = s->test, i = 0; t; t = t->next, i++)
1154 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1155 !FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1156 (FcChar8 *) e->field))
1166 * If there was a test, then replace the matched
1167 * value with the new list of values
1171 FcValueList *thisValue = st[i].value;
1172 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1175 * Append the new list of values after the current value
1177 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1179 * Delete the marked value
1181 FcConfigDel (&st[i].elt->values, thisValue);
1183 * Adjust any pointers into the value list to ensure
1184 * future edits occur at the same place
1186 for (t = s->test, i = 0; t; t = t->next, i++)
1188 if (st[i].value == thisValue)
1189 st[i].value = nextValue;
1193 /* fall through ... */
1194 case FcOpAssignReplace:
1196 * Delete all of the values and insert
1199 FcConfigPatternDel (p, e->field);
1200 FcConfigPatternAdd (p, e->field, l, FcTrue);
1202 * Adjust any pointers into the value list as they no
1203 * longer point to anything valid
1207 FcPatternElt *thisElt = st[i].elt;
1208 for (t = s->test, i = 0; t; t = t->next, i++)
1210 if (st[i].elt == thisElt)
1218 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1221 /* fall through ... */
1222 case FcOpPrependFirst:
1223 FcConfigPatternAdd (p, e->field, l, FcFalse);
1228 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1231 /* fall through ... */
1232 case FcOpAppendLast:
1233 FcConfigPatternAdd (p, e->field, l, FcTrue);
1240 * Now go through the pattern and eliminate
1241 * any properties without data
1243 for (e = s->edit; e; e = e->next)
1244 FcConfigPatternCanon (p, e->field);
1246 if (FcDebug () & FC_DBG_EDIT)
1248 printf ("FcConfigSubstitute edit");
1252 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1254 if (FcDebug () & FC_DBG_EDIT)
1256 printf ("FcConfigSubstitute done");
1263 FcConfigSubstitute (FcConfig *config,
1267 return FcConfigSubstituteWithPat (config, p, 0, kind);
1270 #ifndef FONTCONFIG_PATH
1271 #define FONTCONFIG_PATH "/etc/fonts"
1274 #ifndef FONTCONFIG_FILE
1275 #define FONTCONFIG_FILE "fonts.conf"
1279 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1284 dir = (FcChar8 *) "";
1285 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1289 strcpy ((char *) path, (const char *) dir);
1290 /* make sure there's a single separating / */
1291 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1292 strcat ((char *) path, "/");
1293 strcat ((char *) path, (char *) file);
1295 FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1296 if (access ((char *) path, R_OK) == 0)
1304 FcConfigGetPath (void)
1307 FcChar8 *env, *e, *colon;
1312 npath = 2; /* default dir + null */
1313 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1322 path = calloc (npath, sizeof (FcChar8 *));
1332 colon = (FcChar8 *) strchr ((char *) e, ':');
1334 colon = e + strlen ((char *) e);
1335 path[i] = malloc (colon - e + 1);
1338 strncpy ((char *) path[i], (const char *) e, colon - e);
1339 path[i][colon - e] = '\0';
1348 dir = (FcChar8 *) FONTCONFIG_PATH;
1349 path[i] = malloc (strlen ((char *) dir) + 1);
1352 strcpy ((char *) path[i], (const char *) dir);
1356 for (i = 0; path[i]; i++)
1364 FcConfigFreePath (FcChar8 **path)
1368 for (p = path; *p; p++)
1373 static FcBool _FcConfigHomeEnabled = FcTrue;
1378 if (_FcConfigHomeEnabled)
1379 return getenv ("HOME");
1384 FcConfigEnableHome (FcBool enable)
1386 FcBool prev = _FcConfigHomeEnabled;
1387 _FcConfigHomeEnabled = enable;
1392 FcConfigFilename (const FcChar8 *url)
1394 FcChar8 *file, *dir, **path, **p;
1398 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1400 url = (FcChar8 *) FONTCONFIG_FILE;
1405 dir = FcConfigHome ();
1407 file = FcConfigFileExists (dir, url + 1);
1412 file = FcConfigFileExists (0, url);
1415 path = FcConfigGetPath ();
1418 for (p = path; *p; p++)
1420 file = FcConfigFileExists (*p, url);
1424 FcConfigFreePath (path);
1431 * Manage the application-specific fonts
1435 FcConfigAppFontAddFile (FcConfig *config,
1436 const FcChar8 *file)
1445 config = FcConfigGetCurrent ();
1450 subdirs = FcStrSetCreate ();
1454 set = FcConfigGetFonts (config, FcSetApplication);
1457 set = FcFontSetCreate ();
1460 FcStrSetDestroy (subdirs);
1463 FcConfigSetFonts (config, set, FcSetApplication);
1466 if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1468 FcStrSetDestroy (subdirs);
1471 if ((sublist = FcStrListCreate (subdirs)))
1473 while ((subdir = FcStrListNext (sublist)))
1475 FcConfigAppFontAddDir (config, subdir);
1477 FcStrListDone (sublist);
1483 FcConfigAppFontAddDir (FcConfig *config,
1493 config = FcConfigGetCurrent ();
1497 subdirs = FcStrSetCreate ();
1501 set = FcConfigGetFonts (config, FcSetApplication);
1504 set = FcFontSetCreate ();
1507 FcStrSetDestroy (subdirs);
1510 FcConfigSetFonts (config, set, FcSetApplication);
1513 if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1515 FcStrSetDestroy (subdirs);
1518 if ((sublist = FcStrListCreate (subdirs)))
1520 while ((subdir = FcStrListNext (sublist)))
1522 FcConfigAppFontAddDir (config, subdir);
1524 FcStrListDone (sublist);
1530 FcConfigAppFontClear (FcConfig *config)
1532 FcConfigSetFonts (config, 0, FcSetApplication);