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