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