1 /*
2 * Copyright © 2007 Red Hat, Inc
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Author:
24 * Kristian Høgsberg <krh@redhat.com>
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include "libxfontint.h"
31
32 #ifdef HAVE_READLINK
33 #include <X11/fonts/fntfilst.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <dirent.h>
37 #include <unistd.h>
38 #include "src/util/replace.h"
39
40 static const char CataloguePrefix[] = "catalogue:";
41
42 static int CatalogueFreeFPE (FontPathElementPtr fpe);
43
44 static int
CatalogueNameCheck(const char * name)45 CatalogueNameCheck (const char *name)
46 {
47 return strncmp(name, CataloguePrefix, sizeof(CataloguePrefix) - 1) == 0;
48 }
49
50 typedef struct _CatalogueRec {
51 time_t mtime;
52 int fpeCount, fpeAlloc;
53 FontPathElementPtr *fpeList;
54 } CatalogueRec, *CataloguePtr;
55
56 static int
CatalogueAddFPE(CataloguePtr cat,FontPathElementPtr fpe)57 CatalogueAddFPE (CataloguePtr cat, FontPathElementPtr fpe)
58 {
59 FontPathElementPtr *new;
60
61 if (cat->fpeCount >= cat->fpeAlloc)
62 {
63 if (cat->fpeAlloc == 0)
64 cat->fpeAlloc = 16;
65 else
66 cat->fpeAlloc *= 2;
67
68 new = reallocarray(cat->fpeList, cat->fpeAlloc,
69 sizeof(FontPathElementPtr));
70 if (new == NULL)
71 return AllocError;
72
73 cat->fpeList = new;
74 }
75
76 cat->fpeList[cat->fpeCount++] = fpe;
77
78 return Successful;
79 }
80
81 static const char PriorityAttribute[] = "pri=";
82
83 static int
ComparePriority(const void * p1,const void * p2)84 ComparePriority(const void *p1, const void *p2)
85 {
86 FontDirectoryPtr dir1 = (*(FontPathElementPtr*) p1)->private;
87 FontDirectoryPtr dir2 = (*(FontPathElementPtr*) p2)->private;
88 const char *pri1 = NULL;
89 const char *pri2 = NULL;
90
91 if (dir1->attributes != NULL)
92 pri1 = strstr(dir1->attributes, PriorityAttribute);
93 if (dir2->attributes != NULL)
94 pri2 = strstr(dir2->attributes, PriorityAttribute);
95
96 if (pri1 == NULL && pri2 == NULL)
97 return 0;
98 else if (pri1 == NULL)
99 return 1;
100 else if (pri2 == NULL)
101 return -1;
102 else
103 return
104 atoi(pri1 + strlen(PriorityAttribute)) -
105 atoi(pri2 + strlen(PriorityAttribute));
106 }
107
108 static void
CatalogueUnrefFPEs(FontPathElementPtr fpe)109 CatalogueUnrefFPEs (FontPathElementPtr fpe)
110 {
111 CataloguePtr cat = fpe->private;
112 FontPathElementPtr subfpe;
113 int i;
114
115 for (i = 0; i < cat->fpeCount; i++)
116 {
117 subfpe = cat->fpeList[i];
118 subfpe->refcount--;
119 if (subfpe->refcount == 0)
120 {
121 FontFileFreeFPE (subfpe);
122 free((void *) subfpe->name);
123 free(subfpe);
124 }
125 }
126
127 cat->fpeCount = 0;
128 }
129
130 /* Rescan catalogue directory if modified timestamp has changed or
131 * the forceScan argument says to do it anyway (like on first load). */
132 static int
CatalogueRescan(FontPathElementPtr fpe,Bool forceScan)133 CatalogueRescan (FontPathElementPtr fpe, Bool forceScan)
134 {
135 CataloguePtr cat = fpe->private;
136 char link[MAXFONTFILENAMELEN];
137 char dest[MAXFONTFILENAMELEN];
138 char *attrib;
139 FontPathElementPtr subfpe;
140 struct stat statbuf;
141 const char *path;
142 DIR *dir;
143 struct dirent *entry;
144 int len;
145 int pathlen;
146
147 path = fpe->name + strlen(CataloguePrefix);
148 if (stat(path, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
149 return BadFontPath;
150
151 if ((forceScan == FALSE) && (statbuf.st_mtime <= cat->mtime))
152 return Successful;
153
154 CatalogueUnrefFPEs (fpe);
155
156 dir = opendir(path);
157 if (dir == NULL)
158 return BadFontPath;
159
160 while (entry = readdir(dir), entry != NULL)
161 {
162 char *name;
163 snprintf(link, sizeof link, "%s/%s", path, entry->d_name);
164 len = readlink(link, dest, sizeof dest - 1);
165 if (len < 0)
166 continue;
167
168 dest[len] = '\0';
169
170 if (dest[0] != '/')
171 {
172 pathlen = strlen(path);
173 memmove(dest + pathlen + 1, dest, sizeof dest - pathlen - 1);
174 memcpy(dest, path, pathlen);
175 memcpy(dest + pathlen, "/", 1);
176 len += pathlen + 1;
177 }
178
179 attrib = strchr(link, ':');
180 if (attrib && len + strlen(attrib) < sizeof dest)
181 {
182 memcpy(dest + len, attrib, strlen(attrib));
183 len += strlen(attrib);
184 }
185
186 subfpe = malloc(sizeof *subfpe);
187 if (subfpe == NULL)
188 continue;
189
190 /* The fonts returned by OpenFont will point back to the
191 * subfpe they come from. So set the type of the subfpe to
192 * what the catalogue fpe was assigned, so calls to CloseFont
193 * (which uses font->fpe->type) goes to CatalogueCloseFont. */
194 subfpe->type = fpe->type;
195 subfpe->name_length = len;
196 name = malloc (len + 1);
197 if (name == NULL)
198 {
199 free(subfpe);
200 continue;
201 }
202
203 memcpy(name, dest, len);
204 name[len] = '\0';
205 subfpe->name = name;
206
207 /* The X server will manipulate the subfpe ref counts
208 * associated with the font in OpenFont and CloseFont, so we
209 * have to make sure it's valid. */
210 subfpe->refcount = 1;
211
212 if (FontFileInitFPE (subfpe) != Successful)
213 {
214 free((void *) subfpe->name);
215 free(subfpe);
216 continue;
217 }
218
219 if (CatalogueAddFPE(cat, subfpe) != Successful)
220 {
221 FontFileFreeFPE (subfpe);
222 free(subfpe);
223 continue;
224 }
225 }
226
227 closedir(dir);
228
229 qsort(cat->fpeList,
230 cat->fpeCount, sizeof cat->fpeList[0], ComparePriority);
231
232 cat->mtime = statbuf.st_mtime;
233
234 return Successful;
235 }
236
237 static int
CatalogueInitFPE(FontPathElementPtr fpe)238 CatalogueInitFPE (FontPathElementPtr fpe)
239 {
240 CataloguePtr cat;
241
242 cat = malloc(sizeof *cat);
243 if (cat == NULL)
244 return AllocError;
245
246 fpe->private = (pointer) cat;
247 cat->fpeCount = 0;
248 cat->fpeAlloc = 0;
249 cat->fpeList = NULL;
250 cat->mtime = 0;
251
252 return CatalogueRescan (fpe, TRUE);
253 }
254
255 static int
CatalogueResetFPE(FontPathElementPtr fpe)256 CatalogueResetFPE (FontPathElementPtr fpe)
257 {
258 /* Always just tell the caller to close and re-open */
259 return FPEResetFailed;
260 }
261
262 static int
CatalogueFreeFPE(FontPathElementPtr fpe)263 CatalogueFreeFPE (FontPathElementPtr fpe)
264 {
265 CataloguePtr cat = fpe->private;
266
267 /* If the catalogue is modified while the xserver has fonts open
268 * from the previous subfpes, we'll unref the old subfpes when we
269 * reload the catalogue, and the xserver will the call FreeFPE on
270 * them once it drops its last reference. Thus, the FreeFPE call
271 * for the subfpe ends up here and we just forward it to
272 * FontFileFreeFPE. */
273
274 if (!CatalogueNameCheck (fpe->name))
275 return FontFileFreeFPE (fpe);
276
277 CatalogueUnrefFPEs (fpe);
278 free(cat->fpeList);
279 free(cat);
280
281 return Successful;
282 }
283
284 static int
CatalogueOpenFont(pointer client,FontPathElementPtr fpe,Mask flags,const char * name,int namelen,fsBitmapFormat format,fsBitmapFormatMask fmask,XID id,FontPtr * pFont,char ** aliasName,FontPtr non_cachable_font)285 CatalogueOpenFont (pointer client, FontPathElementPtr fpe, Mask flags,
286 const char *name, int namelen,
287 fsBitmapFormat format, fsBitmapFormatMask fmask,
288 XID id, FontPtr *pFont, char **aliasName,
289 FontPtr non_cachable_font)
290 {
291 CataloguePtr cat = fpe->private;
292 FontPathElementPtr subfpe;
293 int i, status;
294
295 CatalogueRescan (fpe, FALSE);
296
297 for (i = 0; i < cat->fpeCount; i++)
298 {
299 subfpe = cat->fpeList[i];
300 status = FontFileOpenFont(client, subfpe, flags,
301 name, namelen, format, fmask, id,
302 pFont, aliasName, non_cachable_font);
303 if (status == Successful || status == FontNameAlias)
304 return status;
305 }
306
307 return BadFontName;
308 }
309
310 static void
CatalogueCloseFont(FontPathElementPtr fpe,FontPtr pFont)311 CatalogueCloseFont (FontPathElementPtr fpe, FontPtr pFont)
312 {
313 /* Note: this gets called with the actual subfpe where we found
314 * the font, not the catalogue fpe. */
315
316 FontFileCloseFont(fpe, pFont);
317 }
318
319 static int
CatalogueListFonts(pointer client,FontPathElementPtr fpe,const char * pat,int len,int max,FontNamesPtr names)320 CatalogueListFonts (pointer client, FontPathElementPtr fpe, const char *pat,
321 int len, int max, FontNamesPtr names)
322 {
323 CataloguePtr cat = fpe->private;
324 FontPathElementPtr subfpe;
325 int i;
326
327 CatalogueRescan (fpe, FALSE);
328
329 for (i = 0; i < cat->fpeCount; i++)
330 {
331 subfpe = cat->fpeList[i];
332 FontFileListFonts(client, subfpe, pat, len, max, names);
333 }
334
335 return Successful;
336 }
337
338 typedef struct _LFWIData {
339 pointer *privates;
340 int current;
341 } LFWIDataRec, *LFWIDataPtr;
342
343 static int
CatalogueStartListFonts(pointer client,FontPathElementPtr fpe,const char * pat,int len,int max,pointer * privatep,int mark_aliases)344 CatalogueStartListFonts(pointer client, FontPathElementPtr fpe,
345 const char *pat, int len, int max, pointer *privatep,
346 int mark_aliases)
347 {
348 CataloguePtr cat = fpe->private;
349 LFWIDataPtr data;
350 int ret, i, j;
351
352 CatalogueRescan (fpe, FALSE);
353
354 data = malloc (sizeof *data + sizeof data->privates[0] * cat->fpeCount);
355 if (!data)
356 return AllocError;
357 data->privates = (pointer *) (data + 1);
358
359 for (i = 0; i < cat->fpeCount; i++)
360 {
361 ret = FontFileStartListFonts(client, cat->fpeList[i], pat, len,
362 max, &data->privates[i], mark_aliases);
363 if (ret != Successful)
364 goto bail;
365 }
366
367 data->current = 0;
368 *privatep = (pointer) data;
369 return Successful;
370
371 bail:
372 for (j = 0; j < i; j++)
373 /* FIXME: we have no way to free the per-fpe privates. */;
374 free (data);
375
376 return AllocError;
377 }
378
379 static int
CatalogueStartListFontsWithInfo(pointer client,FontPathElementPtr fpe,const char * pat,int len,int max,pointer * privatep)380 CatalogueStartListFontsWithInfo(pointer client, FontPathElementPtr fpe,
381 const char *pat, int len, int max,
382 pointer *privatep)
383 {
384 return CatalogueStartListFonts(client, fpe, pat, len, max, privatep, 0);
385 }
386
387 static int
CatalogueListNextFontWithInfo(pointer client,FontPathElementPtr fpe,char ** namep,int * namelenp,FontInfoPtr * pFontInfo,int * numFonts,pointer private)388 CatalogueListNextFontWithInfo(pointer client, FontPathElementPtr fpe,
389 char **namep, int *namelenp,
390 FontInfoPtr *pFontInfo,
391 int *numFonts, pointer private)
392 {
393 LFWIDataPtr data = private;
394 CataloguePtr cat = fpe->private;
395 int ret;
396
397 if (data->current == cat->fpeCount)
398 {
399 free(data);
400 return BadFontName;
401 }
402
403 ret = FontFileListNextFontWithInfo(client, cat->fpeList[data->current],
404 namep, namelenp,
405 pFontInfo, numFonts,
406 data->privates[data->current]);
407 if (ret == BadFontName)
408 {
409 data->current++;
410 return CatalogueListNextFontWithInfo(client, fpe, namep, namelenp,
411 pFontInfo, numFonts, private);
412 }
413
414 return ret;
415 }
416
417 static int
CatalogueStartListFontsAndAliases(pointer client,FontPathElementPtr fpe,const char * pat,int len,int max,pointer * privatep)418 CatalogueStartListFontsAndAliases(pointer client, FontPathElementPtr fpe,
419 const char *pat, int len, int max,
420 pointer *privatep)
421 {
422 return CatalogueStartListFonts(client, fpe, pat, len, max, privatep, 1);
423 }
424
425 static int
CatalogueListNextFontOrAlias(pointer client,FontPathElementPtr fpe,char ** namep,int * namelenp,char ** resolvedp,int * resolvedlenp,pointer private)426 CatalogueListNextFontOrAlias(pointer client, FontPathElementPtr fpe,
427 char **namep, int *namelenp, char **resolvedp,
428 int *resolvedlenp, pointer private)
429 {
430 LFWIDataPtr data = private;
431 CataloguePtr cat = fpe->private;
432 int ret;
433
434 if (data->current == cat->fpeCount)
435 {
436 free(data);
437 return BadFontName;
438 }
439
440 ret = FontFileListNextFontOrAlias(client, cat->fpeList[data->current],
441 namep, namelenp,
442 resolvedp, resolvedlenp,
443 data->privates[data->current]);
444 if (ret == BadFontName)
445 {
446 data->current++;
447 return CatalogueListNextFontOrAlias(client, fpe, namep, namelenp,
448 resolvedp, resolvedlenp, private);
449 }
450
451 return ret;
452 }
453
454 static const xfont2_fpe_funcs_rec catalogue_fpe_funcs = {
455 .version = XFONT2_FPE_FUNCS_VERSION,
456 .name_check = CatalogueNameCheck,
457 .init_fpe = CatalogueInitFPE,
458 .free_fpe = CatalogueFreeFPE,
459 .reset_fpe = CatalogueResetFPE,
460 .open_font = CatalogueOpenFont,
461 .close_font = CatalogueCloseFont,
462 .list_fonts = CatalogueListFonts,
463 .start_list_fonts_with_info = CatalogueStartListFontsWithInfo,
464 .list_next_font_with_info = CatalogueListNextFontWithInfo,
465 .wakeup_fpe = 0,
466 .client_died = 0,
467 .load_glyphs = 0,
468 .start_list_fonts_and_aliases = CatalogueStartListFontsAndAliases,
469 .list_next_font_or_alias = CatalogueListNextFontOrAlias,
470 .set_path_hook = FontFileEmptyBitmapSource,
471 };
472
473 void
CatalogueRegisterLocalFpeFunctions(void)474 CatalogueRegisterLocalFpeFunctions (void)
475 {
476 register_fpe_funcs(&catalogue_fpe_funcs);
477 }
478
479 #endif /* HAVE_READLINK */
480