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