1 /*
2 
3 Copyright 1991, 1998  The Open Group
4 
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
24 
25 */
26 
27 /*
28  * Author:  Keith Packard, MIT X Consortium
29  */
30 
31 /*
32  * dirfile.c
33  *
34  * Read fonts.dir and fonts.alias files
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40 #include "libxfontint.h"
41 #include <X11/fonts/fntfilst.h>
42 #include <stdio.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <limits.h>
48 #include "src/util/replace.h"
49 
50 static Bool AddFileNameAliases ( FontDirectoryPtr dir );
51 static int ReadFontAlias ( char *directory, Bool isFile,
52 			   FontDirectoryPtr *pdir );
53 static int lexAlias ( FILE *file, char **lexToken );
54 static int lexc ( FILE *file );
55 
56 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
57 
58 int
FontFileReadDirectory(const char * directory,FontDirectoryPtr * pdir)59 FontFileReadDirectory (const char *directory, FontDirectoryPtr *pdir)
60 {
61     char        file_name[MAXFONTFILENAMELEN];
62     char        font_name[MAXFONTNAMELEN];
63     char        dir_file[MAXFONTFILENAMELEN];
64     char	dir_path[MAXFONTFILENAMELEN];
65     char	*ptr;
66     FILE       *file = 0;
67     int         file_fd,
68                 count,
69                 num_fonts,
70                 status;
71     struct stat	statb;
72     static char format[24] = "";
73 #if defined(WIN32)
74     int i;
75 #endif
76 
77     FontDirectoryPtr	dir = NullFontDirectory;
78 
79     if (strlen(directory) + 1 + sizeof(FontDirFile) > sizeof(dir_file))
80 	return BadFontPath;
81 
82     /* Check for font directory attributes */
83 #if !defined(WIN32)
84     if ((ptr = strchr(directory, ':'))) {
85 #else
86     /* OS/2 and WIN32 path might start with a drive letter, don't clip this */
87     if ((ptr = strchr(directory+2, ':'))) {
88 #endif
89 	strncpy(dir_path, directory, ptr - directory);
90 	dir_path[ptr - directory] = '\0';
91     } else {
92 	strlcpy(dir_path, directory, sizeof(dir_path));
93     }
94     strlcpy(dir_file, dir_path, sizeof(dir_file));
95     if (dir_file[strlen(dir_file) - 1] != '/')
96 	strlcat(dir_file, "/", sizeof(dir_file));
97     strlcat(dir_file, FontDirFile, sizeof(dir_file));
98 #ifndef WIN32
99     file_fd = open(dir_file, O_RDONLY | O_NOFOLLOW);
100     if (file_fd >= 0) {
101 	file = fdopen(file_fd, "rt");
102     }
103 #else
104     file = fopen(dir_file, "rt");
105 #endif
106     if (file) {
107 #ifndef WIN32
108 	if (fstat (fileno(file), &statb) == -1)
109 #else
110 	if (stat (dir_file, &statb) == -1)
111 #endif
112         {
113             fclose(file);
114 	    return BadFontPath;
115         }
116 	count = fscanf(file, "%d\n", &num_fonts);
117 	if ((count == EOF) || (count != 1)) {
118 	    fclose(file);
119 	    return BadFontPath;
120 	}
121 	dir = FontFileMakeDir(directory, num_fonts);
122 	if (dir == NULL) {
123 	    fclose(file);
124 	    return BadFontPath;
125 	}
126 	dir->dir_mtime = statb.st_mtime;
127 	if (format[0] == '\0')
128 	    snprintf(format, sizeof(format), "%%%ds %%%d[^\n]\n",
129 		     MAXFONTFILENAMELEN-1, MAXFONTNAMELEN-1);
130 
131 	while ((count = fscanf(file, format, file_name, font_name)) != EOF) {
132 #if defined(WIN32)
133 	    /* strip any existing trailing CR */
134 	    for (i=0; i<strlen(font_name); i++) {
135 		if (font_name[i]=='\r') font_name[i] = '\0';
136 	    }
137 #endif
138 	    if (count != 2) {
139 		FontFileFreeDir (dir);
140 		fclose(file);
141 		return BadFontPath;
142 	    }
143 
144 	    /*
145 	     * We blindly try to load all the font files specified.
146 	     * In theory, we might want to warn that some of the fonts
147 	     * couldn't be loaded.
148 	     */
149 	    FontFileAddFontFile (dir, font_name, file_name);
150 	}
151 	fclose(file);
152 
153     } else if (errno != ENOENT) {
154 	return BadFontPath;
155     }
156     status = ReadFontAlias(dir_path, FALSE, &dir);
157     if (status != Successful) {
158 	if (dir)
159 	    FontFileFreeDir (dir);
160 	return status;
161     }
162     if (!dir)
163 	return BadFontPath;
164 
165     FontFileSortDir(dir);
166 
167     *pdir = dir;
168     return Successful;
169 }
170 
171 Bool
172 FontFileDirectoryChanged(FontDirectoryPtr dir)
173 {
174     char	dir_file[MAXFONTFILENAMELEN];
175     struct stat	statb;
176 
177     if (strlen(dir->directory) + sizeof(FontDirFile) > sizeof(dir_file))
178 	return FALSE;
179 
180     strlcpy (dir_file, dir->directory, sizeof(dir_file));
181     strlcat (dir_file, FontDirFile, sizeof(dir_file));
182     if (stat (dir_file, &statb) == -1)
183     {
184 	if (errno != ENOENT || dir->dir_mtime != 0)
185 	    return TRUE;
186 	return FALSE;		/* doesn't exist and never did: no change */
187     }
188     if (dir->dir_mtime != statb.st_mtime)
189 	return TRUE;
190 
191     if ((strlen(dir->directory) + sizeof(FontAliasFile)) > sizeof(dir_file))
192 	return FALSE;
193     strlcpy (dir_file, dir->directory, sizeof(dir_file));
194     strlcat (dir_file, FontAliasFile, sizeof(dir_file));
195     if (stat (dir_file, &statb) == -1)
196     {
197 	if (errno != ENOENT || dir->alias_mtime != 0)
198 	    return TRUE;
199 	return FALSE;		/* doesn't exist and never did: no change */
200     }
201     if (dir->alias_mtime != statb.st_mtime)
202 	return TRUE;
203     return FALSE;
204 }
205 
206 /*
207  * Make each of the file names an automatic alias for each of the files.
208  */
209 
210 static Bool
211 AddFileNameAliases(FontDirectoryPtr dir)
212 {
213     int		    i;
214     char	    copy[MAXFONTFILENAMELEN];
215     char	    *fileName;
216     FontTablePtr    table;
217     FontRendererPtr renderer;
218     int		    len;
219     FontNameRec	    name;
220 
221     table = &dir->nonScalable;
222     for (i = 0; i < table->used; i++) {
223 	if (table->entries[i].type != FONT_ENTRY_BITMAP)
224 	    continue;
225 	fileName = table->entries[i].u.bitmap.fileName;
226 	renderer = FontFileMatchRenderer (fileName);
227 	if (!renderer)
228 	    continue;
229 
230 	len = strlen (fileName) - renderer->fileSuffixLen;
231 	if (len >= sizeof(copy))
232 	    continue;
233 	CopyISOLatin1Lowered (copy, fileName, len);
234 	copy[len] = '\0';
235 	name.name = copy;
236 	name.length = len;
237 	name.ndashes = FontFileCountDashes (copy, len);
238 
239 	if (!FontFileFindNameInDir(table, &name)) {
240 	    if (!FontFileAddFontAlias (dir, copy, table->entries[i].name.name))
241 		return FALSE;
242 	}
243     }
244     return TRUE;
245 }
246 
247 /*
248  * parse the font.alias file.  Format is:
249  *
250  * alias font-name
251  *
252  * To imbed white-space in an alias name, enclose it like "font name"
253  * in double quotes.  \ escapes and character, so
254  * "font name \"With Double Quotes\" \\ and \\ back-slashes"
255  * works just fine.
256  *
257  * A line beginning with a ! denotes a newline-terminated comment.
258  */
259 
260 /*
261  * token types
262  */
263 
264 #define NAME		0
265 #define NEWLINE		1
266 #define DONE		2
267 #define EALLOC		3
268 
269 static int
270 ReadFontAlias(char *directory, Bool isFile, FontDirectoryPtr *pdir)
271 {
272     char		alias[MAXFONTNAMELEN];
273     char		font_name[MAXFONTNAMELEN];
274     char		alias_file[MAXFONTFILENAMELEN];
275     int			file_fd;
276     FILE		*file = 0;
277     FontDirectoryPtr	dir;
278     int			token;
279     char		*lexToken;
280     int			status = Successful;
281     struct stat		statb;
282 
283     if (strlen(directory) >= sizeof(alias_file))
284 	return BadFontPath;
285     dir = *pdir;
286     strlcpy(alias_file, directory, sizeof(alias_file));
287     if (!isFile) {
288 	if (strlen(directory) + 1 + sizeof(FontAliasFile) > sizeof(alias_file))
289 	    return BadFontPath;
290 	if (directory[strlen(directory) - 1] != '/')
291 	    strlcat(alias_file, "/", sizeof(alias_file));
292 	strlcat(alias_file, FontAliasFile, sizeof(alias_file));
293     }
294 
295 #ifndef WIN32
296     file_fd = open(alias_file, O_RDONLY | O_NOFOLLOW);
297     if (file_fd >= 0) {
298 	file = fdopen(file_fd, "rt");
299     }
300 #else
301     file = fopen(alias_file, "rt");
302 #endif
303 
304     if (!file)
305 	return ((errno == ENOENT) ? Successful : BadFontPath);
306     if (!dir)
307 	*pdir = dir = FontFileMakeDir(directory, 10);
308     if (!dir)
309     {
310 	fclose (file);
311 	return AllocError;
312     }
313 #ifndef WIN32
314     if (fstat (fileno (file), &statb) == -1)
315 #else
316     if (stat (alias_file, &statb) == -1)
317 #endif
318     {
319 	fclose (file);
320 	return BadFontPath;
321     }
322     dir->alias_mtime = statb.st_mtime;
323     while (status == Successful) {
324 	token = lexAlias(file, &lexToken);
325 	switch (token) {
326 	case NEWLINE:
327 	    break;
328 	case DONE:
329 	    fclose(file);
330 	    return Successful;
331 	case EALLOC:
332 	    status = AllocError;
333 	    break;
334 	case NAME:
335 	    if (strlen(lexToken) >= sizeof(alias)) {
336 		status = BadFontPath;
337 		break;
338 	    }
339 	    strlcpy(alias, lexToken, sizeof(alias));
340 	    token = lexAlias(file, &lexToken);
341 	    switch (token) {
342 	    case NEWLINE:
343 		if (strcmp(alias, "FILE_NAMES_ALIASES"))
344 		    status = BadFontPath;
345 		else if (!AddFileNameAliases(dir))
346 		    status = AllocError;
347 		break;
348 	    case DONE:
349 		status = BadFontPath;
350 		break;
351 	    case EALLOC:
352 		status = AllocError;
353 		break;
354 	    case NAME:
355 		if (strlen(lexToken) >= sizeof(font_name)) {
356 		    status = BadFontPath;
357 		    break;
358 		}
359 		CopyISOLatin1Lowered(alias, alias, strlen(alias));
360 		CopyISOLatin1Lowered(font_name, lexToken, strlen(lexToken));
361 		if (!FontFileAddFontAlias (dir, alias, font_name))
362 		    status = AllocError;
363 		break;
364 	    }
365 	}
366     }
367     fclose(file);
368     return status;
369 }
370 
371 #define QUOTE		0
372 #define WHITE		1
373 #define NORMAL		2
374 #define END		3
375 #define NL		4
376 #define BANG		5
377 
378 static int  charClass;
379 
380 static int
381 lexAlias(FILE *file, char **lexToken)
382 {
383     int         c;
384     char       *t;
385     enum state {
386 	Begin, Normal, Quoted, Comment
387     }           state;
388     int         count;
389 
390     static char *tokenBuf = (char *) NULL;
391     static int  tokenSize = 0;
392 
393     t = tokenBuf;
394     count = 0;
395     state = Begin;
396     for (;;) {
397 	if (count == tokenSize) {
398 	    int         nsize;
399 	    char       *nbuf;
400 
401 	    if (tokenSize >= (INT_MAX >> 2))
402 		/* Stop before we overflow */
403 		return EALLOC;
404 	    nsize = tokenSize ? (tokenSize << 1) : 64;
405 	    nbuf = realloc(tokenBuf, nsize);
406 	    if (!nbuf)
407 		return EALLOC;
408 	    tokenBuf = nbuf;
409 	    tokenSize = nsize;
410 	    t = tokenBuf + count;
411 	}
412 	c = lexc(file);
413 	switch (charClass) {
414 	case QUOTE:
415 	    switch (state) {
416 	    case Begin:
417 	    case Normal:
418 		state = Quoted;
419 		break;
420 	    case Quoted:
421 		state = Normal;
422 		break;
423 	    case Comment:
424 		break;
425 	    }
426 	    break;
427 	case WHITE:
428 	    switch (state) {
429 	    case Begin:
430 	    case Comment:
431 		continue;
432 	    case Normal:
433 		*t = '\0';
434 		*lexToken = tokenBuf;
435 		return NAME;
436 	    case Quoted:
437 		break;
438 	    }
439 	    /* fall through */
440 	case NORMAL:
441 	    switch (state) {
442 	    case Begin:
443 		state = Normal;
444 		break;
445 	    case Comment:
446 		continue;
447 	    default:
448 		break;
449 	    }
450 	    *t++ = c;
451 	    ++count;
452 	    break;
453 	case END:
454 	case NL:
455 	    switch (state) {
456 	    case Begin:
457 	    case Comment:
458 		*lexToken = (char *) NULL;
459 		return charClass == END ? DONE : NEWLINE;
460 	    default:
461 		*t = '\0';
462 		*lexToken = tokenBuf;
463 		ungetc(c, file);
464 		return NAME;
465 	    }
466 	    break;
467 	case BANG:
468 	    switch (state) {
469 	    case Begin:
470 		state = Comment;
471 		break;
472             case Comment:
473 		break;
474             default:
475 		*t++ = c;
476 		++count;
477 	    }
478 	    break;
479 	}
480     }
481 }
482 
483 static int
484 lexc(FILE *file)
485 {
486     int         c;
487 
488     c = getc(file);
489     switch (c) {
490     case EOF:
491 	charClass = END;
492 	break;
493     case '\\':
494 	c = getc(file);
495 	if (c == EOF)
496 	    charClass = END;
497 	else
498 	    charClass = NORMAL;
499 	break;
500     case '"':
501 	charClass = QUOTE;
502 	break;
503     case ' ':
504     case '\t':
505 	charClass = WHITE;
506 	break;
507     case '\r':
508     case '\n':
509 	charClass = NL;
510 	break;
511     case '!':
512 	charClass = BANG;
513 	break;
514     default:
515 	charClass = NORMAL;
516 	break;
517     }
518     return c;
519 }
520