2 * $XFree86: xc/lib/fontconfig/src/fccfg.c,v 1.23 2002/08/31 22:17:32 keithp 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 (const FcValue m_o,
492 FcBool ret = FcFalse;
494 m = FcConfigPromote (m, v);
495 v = FcConfigPromote (v, m);
496 if (m.type == v.type)
500 break; /* FcConfigPromote prevents this from happening */
505 ret = m.u.d == v.u.d;
508 case FcOpNotContains:
509 ret = m.u.d != v.u.d;
515 ret = m.u.d <= v.u.d;
521 ret = m.u.d >= v.u.d;
531 ret = m.u.b == v.u.b;
534 case FcOpNotContains:
535 ret = m.u.b != v.u.b;
545 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
548 case FcOpNotContains:
549 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
559 ret = FcMatrixEqual (m.u.m, v.u.m);
562 case FcOpNotContains:
563 ret = !FcMatrixEqual (m.u.m, v.u.m);
572 /* m contains v if v is a subset of m */
573 ret = FcCharSetIsSubset (v.u.c, m.u.c);
575 case FcOpNotContains:
576 /* m contains v if v is a subset of m */
577 ret = !FcCharSetIsSubset (v.u.c, m.u.c);
580 ret = FcCharSetEqual (m.u.c, v.u.c);
583 ret = !FcCharSetEqual (m.u.c, v.u.c);
592 ret = FcLangSetCompare (v.u.l, m.u.l) != FcLangDifferentLang;
594 case FcOpNotContains:
595 ret = FcLangSetCompare (v.u.l, m.u.l) == FcLangDifferentLang;
598 ret = FcLangSetEqual (v.u.l, m.u.l);
601 ret = !FcLangSetEqual (v.u.l, m.u.l);
621 ret = m.u.f == v.u.f;
624 case FcOpNotContains:
625 ret = m.u.f != v.u.f;
635 if (op == FcOpNotEqual || op == FcOpNotContains)
643 FcConfigEvaluate (FcPattern *p, FcExpr *e)
651 v.type = FcTypeInteger;
655 v.type = FcTypeDouble;
659 v.type = FcTypeString;
664 v.type = FcTypeMatrix;
669 v.type = FcTypeCharSet;
678 r = FcPatternGet (p, e->u.field, 0, &v);
679 if (r != FcResultMatch)
683 if (FcNameConstant (e->u.constant, &v.u.i))
684 v.type = FcTypeInteger;
689 vl = FcConfigEvaluate (p, e->u.tree.left);
690 if (vl.type == FcTypeBool)
693 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
695 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
708 case FcOpNotContains:
709 vl = FcConfigEvaluate (p, e->u.tree.left);
710 vr = FcConfigEvaluate (p, e->u.tree.right);
712 v.u.b = FcConfigCompareValue (vl, e->op, vr);
722 vl = FcConfigEvaluate (p, e->u.tree.left);
723 vr = FcConfigEvaluate (p, e->u.tree.right);
724 vl = FcConfigPromote (vl, vr);
725 vr = FcConfigPromote (vr, vl);
726 if (vl.type == vr.type)
732 v.type = FcTypeDouble;
733 v.u.d = vl.u.d + vr.u.d;
736 v.type = FcTypeDouble;
737 v.u.d = vl.u.d - vr.u.d;
740 v.type = FcTypeDouble;
741 v.u.d = vl.u.d * vr.u.d;
744 v.type = FcTypeDouble;
745 v.u.d = vl.u.d / vr.u.d;
751 if (v.type == FcTypeDouble &&
752 v.u.d == (double) (int) v.u.d)
754 v.type = FcTypeInteger;
762 v.u.b = vl.u.b || vr.u.b;
766 v.u.b = vl.u.b && vr.u.b;
776 v.type = FcTypeString;
777 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
789 v.type = FcTypeMatrix;
790 m = malloc (sizeof (FcMatrix));
793 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
794 FcMatrixMultiply (m, vl.u.m, vr.u.m);
818 vl = FcConfigEvaluate (p, e->u.tree.left);
838 FcConfigMatchValueList (FcPattern *p,
842 FcValueList *ret = 0;
849 if (e->op == FcOpComma)
851 value = FcConfigEvaluate (p, e->u.tree.left);
856 value = FcConfigEvaluate (p, e);
860 for (v = values; v; v = v->next)
862 if (FcConfigCompareValue (v->value, t->op, value))
869 if (t->qual == FcQualAll)
876 FcValueDestroy (value);
882 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
888 l = (FcValueList *) malloc (sizeof (FcValueList));
891 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
892 if (e->op == FcOpComma)
894 l->value = FcConfigEvaluate (p, e->u.tree.left);
895 l->next = FcConfigValues (p, e->u.tree.right, binding);
899 l->value = FcConfigEvaluate (p, e);
902 l->binding = binding;
903 while (l && l->value.type == FcTypeVoid)
905 FcValueList *next = l->next;
907 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
915 FcConfigAdd (FcValueList **head,
916 FcValueList *position,
920 FcValueList **prev, *last, *v;
921 FcValueBinding sameBinding;
924 sameBinding = position->binding;
926 sameBinding = FcValueBindingWeak;
927 for (v = new; v; v = v->next)
928 if (v->binding == FcValueBindingSame)
929 v->binding = sameBinding;
933 prev = &position->next;
935 for (prev = head; *prev; prev = &(*prev)->next)
942 for (prev = head; *prev; prev = &(*prev)->next)
944 if (*prev == position)
951 if (FcDebug () & FC_DBG_EDIT)
954 printf ("position not on list\n");
958 if (FcDebug () & FC_DBG_EDIT)
960 printf ("%s list before ", append ? "Append" : "Prepend");
961 FcValueListPrint (*head);
975 if (FcDebug () & FC_DBG_EDIT)
977 printf ("%s list after ", append ? "Append" : "Prepend");
978 FcValueListPrint (*head);
986 FcConfigDel (FcValueList **head,
987 FcValueList *position)
991 for (prev = head; *prev; prev = &(*prev)->next)
993 if (*prev == position)
995 *prev = position->next;
997 FcValueListDestroy (position);
1004 FcConfigPatternAdd (FcPattern *p,
1011 FcPatternElt *e = FcPatternInsertElt (p, object);
1015 FcConfigAdd (&e->values, 0, append, list);
1020 * Delete all values associated with a field
1023 FcConfigPatternDel (FcPattern *p,
1026 FcPatternElt *e = FcPatternFindElt (p, object);
1030 FcConfigDel (&e->values, e->values);
1034 FcConfigPatternCanon (FcPattern *p,
1037 FcPatternElt *e = FcPatternFindElt (p, object);
1041 FcPatternDel (p, object);
1045 FcConfigSubstituteWithPat (FcConfig *config,
1060 config = FcConfigGetCurrent ();
1065 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1066 if (!st && config->maxObjects)
1068 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1070 if (FcDebug () & FC_DBG_EDIT)
1072 printf ("FcConfigSubstitute ");
1075 if (kind == FcMatchPattern)
1076 s = config->substPattern;
1078 s = config->substFont;
1079 for (; s; s = s->next)
1082 * Check the tests to see if
1083 * they all match the pattern
1085 for (t = s->test, i = 0; t; t = t->next, i++)
1087 if (FcDebug () & FC_DBG_EDIT)
1089 printf ("FcConfigSubstitute test ");
1093 if (kind == FcMatchFont && t->kind == FcMatchPattern)
1098 st[i].elt = FcPatternFindElt (m, t->field);
1102 * If there's no such field in the font,
1103 * then FcQualAll matches while FcQualAny does not
1107 if (t->qual == FcQualAll)
1116 * Check to see if there is a match, mark the location
1117 * to apply match-relative edits
1119 st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1122 if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1124 if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1129 if (FcDebug () & FC_DBG_EDIT)
1130 printf ("No match\n");
1133 if (FcDebug () & FC_DBG_EDIT)
1135 printf ("Substitute ");
1138 for (e = s->edit; e; e = e->next)
1141 * Evaluate the list of expressions
1143 l = FcConfigValues (p, e->expr, e->binding);
1145 * Locate any test associated with this field, skipping
1146 * tests associated with the pattern when substituting in
1149 for (t = s->test, i = 0; t; t = t->next, i++)
1151 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1152 !FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1153 (FcChar8 *) e->field))
1163 * If there was a test, then replace the matched
1164 * value with the new list of values
1168 FcValueList *thisValue = st[i].value;
1169 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1172 * Append the new list of values after the current value
1174 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1176 * Delete the marked value
1178 FcConfigDel (&st[i].elt->values, thisValue);
1180 * Adjust any pointers into the value list to ensure
1181 * future edits occur at the same place
1183 for (t = s->test, i = 0; t; t = t->next, i++)
1185 if (st[i].value == thisValue)
1186 st[i].value = nextValue;
1190 /* fall through ... */
1191 case FcOpAssignReplace:
1193 * Delete all of the values and insert
1196 FcConfigPatternDel (p, e->field);
1197 FcConfigPatternAdd (p, e->field, l, FcTrue);
1199 * Adjust any pointers into the value list as they no
1200 * longer point to anything valid
1204 FcPatternElt *thisElt = st[i].elt;
1205 for (t = s->test, i = 0; t; t = t->next, i++)
1207 if (st[i].elt == thisElt)
1215 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1218 /* fall through ... */
1219 case FcOpPrependFirst:
1220 FcConfigPatternAdd (p, e->field, l, FcFalse);
1225 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1228 /* fall through ... */
1229 case FcOpAppendLast:
1230 FcConfigPatternAdd (p, e->field, l, FcTrue);
1237 * Now go through the pattern and eliminate
1238 * any properties without data
1240 for (e = s->edit; e; e = e->next)
1241 FcConfigPatternCanon (p, e->field);
1243 if (FcDebug () & FC_DBG_EDIT)
1245 printf ("FcConfigSubstitute edit");
1249 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1251 if (FcDebug () & FC_DBG_EDIT)
1253 printf ("FcConfigSubstitute done");
1260 FcConfigSubstitute (FcConfig *config,
1264 return FcConfigSubstituteWithPat (config, p, 0, kind);
1267 #ifndef FONTCONFIG_PATH
1268 #define FONTCONFIG_PATH "/etc/fonts"
1271 #ifndef FONTCONFIG_FILE
1272 #define FONTCONFIG_FILE "fonts.conf"
1276 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1281 dir = (FcChar8 *) "";
1282 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1286 strcpy ((char *) path, (const char *) dir);
1287 /* make sure there's a single separating / */
1288 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1289 strcat ((char *) path, "/");
1290 strcat ((char *) path, (char *) file);
1292 FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1293 if (access ((char *) path, R_OK) == 0)
1301 FcConfigGetPath (void)
1304 FcChar8 *env, *e, *colon;
1309 npath = 2; /* default dir + null */
1310 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1319 path = calloc (npath, sizeof (FcChar8 *));
1329 colon = (FcChar8 *) strchr ((char *) e, ':');
1331 colon = e + strlen ((char *) e);
1332 path[i] = malloc (colon - e + 1);
1335 strncpy ((char *) path[i], (const char *) e, colon - e);
1336 path[i][colon - e] = '\0';
1345 dir = (FcChar8 *) FONTCONFIG_PATH;
1346 path[i] = malloc (strlen ((char *) dir) + 1);
1349 strcpy ((char *) path[i], (const char *) dir);
1353 for (i = 0; path[i]; i++)
1361 FcConfigFreePath (FcChar8 **path)
1365 for (p = path; *p; p++)
1371 FcConfigFilename (const FcChar8 *url)
1373 FcChar8 *file, *dir, **path, **p;
1377 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1379 url = (FcChar8 *) FONTCONFIG_FILE;
1384 dir = (FcChar8 *) getenv ("HOME");
1386 file = FcConfigFileExists (dir, url + 1);
1391 file = FcConfigFileExists (0, url);
1394 path = FcConfigGetPath ();
1397 for (p = path; *p; p++)
1399 file = FcConfigFileExists (*p, url);
1403 FcConfigFreePath (path);
1410 * Manage the application-specific fonts
1414 FcConfigAppFontAddFile (FcConfig *config,
1415 const FcChar8 *file)
1424 config = FcConfigGetCurrent ();
1429 subdirs = FcStrSetCreate ();
1433 set = FcConfigGetFonts (config, FcSetApplication);
1436 set = FcFontSetCreate ();
1439 FcStrSetDestroy (subdirs);
1442 FcConfigSetFonts (config, set, FcSetApplication);
1445 if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1447 FcStrSetDestroy (subdirs);
1450 if ((sublist = FcStrListCreate (subdirs)))
1452 while ((subdir = FcStrListNext (sublist)))
1454 FcConfigAppFontAddDir (config, subdir);
1456 FcStrListDone (sublist);
1462 FcConfigAppFontAddDir (FcConfig *config,
1472 config = FcConfigGetCurrent ();
1476 subdirs = FcStrSetCreate ();
1480 set = FcConfigGetFonts (config, FcSetApplication);
1483 set = FcFontSetCreate ();
1486 FcStrSetDestroy (subdirs);
1489 FcConfigSetFonts (config, set, FcSetApplication);
1492 if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1494 FcStrSetDestroy (subdirs);
1497 if ((sublist = FcStrListCreate (subdirs)))
1499 while ((subdir = FcStrListNext (sublist)))
1501 FcConfigAppFontAddDir (config, subdir);
1503 FcStrListDone (sublist);
1509 FcConfigAppFontClear (FcConfig *config)
1511 FcConfigSetFonts (config, 0, FcSetApplication);