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