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