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