123a0898aSmrg/* 223a0898aSmrg 323a0898aSmrgCopyright 1991, 1998 The Open Group 423a0898aSmrg 523a0898aSmrgPermission to use, copy, modify, distribute, and sell this software and its 623a0898aSmrgdocumentation for any purpose is hereby granted without fee, provided that 723a0898aSmrgthe above copyright notice appear in all copies and that both that 823a0898aSmrgcopyright notice and this permission notice appear in supporting 923a0898aSmrgdocumentation. 1023a0898aSmrg 1123a0898aSmrgThe above copyright notice and this permission notice shall be included in 1223a0898aSmrgall copies or substantial portions of the Software. 1323a0898aSmrg 1423a0898aSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1523a0898aSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1623a0898aSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1723a0898aSmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 1823a0898aSmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 1923a0898aSmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2023a0898aSmrg 2123a0898aSmrgExcept as contained in this notice, the name of The Open Group shall not be 2223a0898aSmrgused in advertising or otherwise to promote the sale, use or other dealings 2323a0898aSmrgin this Software without prior written authorization from The Open Group. 2423a0898aSmrg 2523a0898aSmrg*/ 2623a0898aSmrg 2723a0898aSmrg/* 2823a0898aSmrg * Author: Keith Packard, MIT X Consortium 2923a0898aSmrg */ 3023a0898aSmrg 3123a0898aSmrg/* 3223a0898aSmrg * dirfile.c 3323a0898aSmrg * 3423a0898aSmrg * Read fonts.dir and fonts.alias files 3523a0898aSmrg */ 3623a0898aSmrg 3723a0898aSmrg#ifdef HAVE_CONFIG_H 3823a0898aSmrg#include <config.h> 3923a0898aSmrg#endif 4023a0898aSmrg#include <X11/fonts/fntfilst.h> 4123a0898aSmrg#include <stdio.h> 4223a0898aSmrg#include <sys/types.h> 4323a0898aSmrg#include <sys/stat.h> 44f262af57Smrg#include <fcntl.h> 4523a0898aSmrg#include <errno.h> 469d21a897Sspz#include <limits.h> 4723a0898aSmrg 4823a0898aSmrgstatic Bool AddFileNameAliases ( FontDirectoryPtr dir ); 4923a0898aSmrgstatic int ReadFontAlias ( char *directory, Bool isFile, 5023a0898aSmrg FontDirectoryPtr *pdir ); 5123a0898aSmrgstatic int lexAlias ( FILE *file, char **lexToken ); 5223a0898aSmrgstatic int lexc ( FILE *file ); 5323a0898aSmrg 540b332824Ssnj#pragma GCC diagnostic ignored "-Wformat-nonliteral" 550b332824Ssnj 5623a0898aSmrgint 570b332824SsnjFontFileReadDirectory (const char *directory, FontDirectoryPtr *pdir) 5823a0898aSmrg{ 5923a0898aSmrg char file_name[MAXFONTFILENAMELEN]; 6023a0898aSmrg char font_name[MAXFONTNAMELEN]; 6123a0898aSmrg char dir_file[MAXFONTFILENAMELEN]; 6223a0898aSmrg char dir_path[MAXFONTFILENAMELEN]; 6323a0898aSmrg char *ptr; 64f262af57Smrg FILE *file = 0; 65f262af57Smrg int file_fd, 66f262af57Smrg count, 6723a0898aSmrg num_fonts, 6823a0898aSmrg status; 6923a0898aSmrg struct stat statb; 7023a0898aSmrg static char format[24] = ""; 717f7f5e4eSmrg#if defined(WIN32) 727f7f5e4eSmrg int i; 737f7f5e4eSmrg#endif 7423a0898aSmrg 7523a0898aSmrg FontDirectoryPtr dir = NullFontDirectory; 7623a0898aSmrg 7723a0898aSmrg if (strlen(directory) + 1 + sizeof(FontDirFile) > sizeof(dir_file)) 7823a0898aSmrg return BadFontPath; 7923a0898aSmrg 8023a0898aSmrg /* Check for font directory attributes */ 817f7f5e4eSmrg#if !defined(WIN32) 8223a0898aSmrg if ((ptr = strchr(directory, ':'))) { 8323a0898aSmrg#else 8423a0898aSmrg /* OS/2 and WIN32 path might start with a drive letter, don't clip this */ 8523a0898aSmrg if ((ptr = strchr(directory+2, ':'))) { 8623a0898aSmrg#endif 8723a0898aSmrg strncpy(dir_path, directory, ptr - directory); 8823a0898aSmrg dir_path[ptr - directory] = '\0'; 8923a0898aSmrg } else { 9023a0898aSmrg strcpy(dir_path, directory); 9123a0898aSmrg } 9223a0898aSmrg strcpy(dir_file, dir_path); 9323a0898aSmrg if (dir_file[strlen(dir_file) - 1] != '/') 9423a0898aSmrg strcat(dir_file, "/"); 9523a0898aSmrg strcat(dir_file, FontDirFile); 96f262af57Smrg#ifndef WIN32 97f262af57Smrg file_fd = open(dir_file, O_RDONLY | O_NOFOLLOW); 98f262af57Smrg if (file_fd >= 0) { 99f262af57Smrg file = fdopen(file_fd, "rt"); 100f262af57Smrg } 101f262af57Smrg#else 10223a0898aSmrg file = fopen(dir_file, "rt"); 103f262af57Smrg#endif 10423a0898aSmrg if (file) { 10541c30155Smrg#ifndef WIN32 10623a0898aSmrg if (fstat (fileno(file), &statb) == -1) 10723a0898aSmrg#else 10823a0898aSmrg if (stat (dir_file, &statb) == -1) 10923a0898aSmrg#endif 11023a0898aSmrg { 11123a0898aSmrg fclose(file); 11223a0898aSmrg return BadFontPath; 11323a0898aSmrg } 11423a0898aSmrg count = fscanf(file, "%d\n", &num_fonts); 11523a0898aSmrg if ((count == EOF) || (count != 1)) { 11623a0898aSmrg fclose(file); 11723a0898aSmrg return BadFontPath; 11823a0898aSmrg } 11923a0898aSmrg dir = FontFileMakeDir(directory, num_fonts); 12023a0898aSmrg if (dir == NULL) { 12123a0898aSmrg fclose(file); 12223a0898aSmrg return BadFontPath; 12323a0898aSmrg } 12423a0898aSmrg dir->dir_mtime = statb.st_mtime; 12523a0898aSmrg if (format[0] == '\0') 12623a0898aSmrg sprintf(format, "%%%ds %%%d[^\n]\n", 12723a0898aSmrg MAXFONTFILENAMELEN-1, MAXFONTNAMELEN-1); 12823a0898aSmrg 12923a0898aSmrg while ((count = fscanf(file, format, file_name, font_name)) != EOF) { 1307f7f5e4eSmrg#if defined(WIN32) 13123a0898aSmrg /* strip any existing trailing CR */ 13223a0898aSmrg for (i=0; i<strlen(font_name); i++) { 13323a0898aSmrg if (font_name[i]=='\r') font_name[i] = '\0'; 13423a0898aSmrg } 13523a0898aSmrg#endif 13623a0898aSmrg if (count != 2) { 13723a0898aSmrg FontFileFreeDir (dir); 13823a0898aSmrg fclose(file); 13923a0898aSmrg return BadFontPath; 14023a0898aSmrg } 14123a0898aSmrg 14223a0898aSmrg /* 14323a0898aSmrg * We blindly try to load all the font files specified. 14423a0898aSmrg * In theory, we might want to warn that some of the fonts 14523a0898aSmrg * couldn't be loaded. 14623a0898aSmrg */ 14723a0898aSmrg FontFileAddFontFile (dir, font_name, file_name); 14823a0898aSmrg } 14923a0898aSmrg fclose(file); 15041c30155Smrg 15123a0898aSmrg } else if (errno != ENOENT) { 15223a0898aSmrg return BadFontPath; 15323a0898aSmrg } 15423a0898aSmrg status = ReadFontAlias(dir_path, FALSE, &dir); 15523a0898aSmrg if (status != Successful) { 15623a0898aSmrg if (dir) 15723a0898aSmrg FontFileFreeDir (dir); 15823a0898aSmrg return status; 15923a0898aSmrg } 16023a0898aSmrg if (!dir) 16123a0898aSmrg return BadFontPath; 16223a0898aSmrg 16323a0898aSmrg FontFileSortDir(dir); 16423a0898aSmrg 16523a0898aSmrg *pdir = dir; 16623a0898aSmrg return Successful; 16723a0898aSmrg} 16823a0898aSmrg 16923a0898aSmrgBool 17023a0898aSmrgFontFileDirectoryChanged(FontDirectoryPtr dir) 17123a0898aSmrg{ 17223a0898aSmrg char dir_file[MAXFONTFILENAMELEN]; 17323a0898aSmrg struct stat statb; 17423a0898aSmrg 17523a0898aSmrg if (strlen(dir->directory) + sizeof(FontDirFile) > sizeof(dir_file)) 17623a0898aSmrg return FALSE; 17723a0898aSmrg 17823a0898aSmrg strcpy (dir_file, dir->directory); 17923a0898aSmrg strcat (dir_file, FontDirFile); 18023a0898aSmrg if (stat (dir_file, &statb) == -1) 18123a0898aSmrg { 18223a0898aSmrg if (errno != ENOENT || dir->dir_mtime != 0) 18323a0898aSmrg return TRUE; 18423a0898aSmrg return FALSE; /* doesn't exist and never did: no change */ 18523a0898aSmrg } 18623a0898aSmrg if (dir->dir_mtime != statb.st_mtime) 18723a0898aSmrg return TRUE; 1887f7f5e4eSmrg 1897f7f5e4eSmrg if ((strlen(dir->directory) + sizeof(FontAliasFile)) > sizeof(dir_file)) 1907f7f5e4eSmrg return FALSE; 19123a0898aSmrg strcpy (dir_file, dir->directory); 19223a0898aSmrg strcat (dir_file, FontAliasFile); 19323a0898aSmrg if (stat (dir_file, &statb) == -1) 19423a0898aSmrg { 19523a0898aSmrg if (errno != ENOENT || dir->alias_mtime != 0) 19623a0898aSmrg return TRUE; 19723a0898aSmrg return FALSE; /* doesn't exist and never did: no change */ 19823a0898aSmrg } 19923a0898aSmrg if (dir->alias_mtime != statb.st_mtime) 20023a0898aSmrg return TRUE; 20123a0898aSmrg return FALSE; 20223a0898aSmrg} 20341c30155Smrg 20423a0898aSmrg/* 20523a0898aSmrg * Make each of the file names an automatic alias for each of the files. 20623a0898aSmrg */ 20723a0898aSmrg 20823a0898aSmrgstatic Bool 20923a0898aSmrgAddFileNameAliases(FontDirectoryPtr dir) 21023a0898aSmrg{ 21123a0898aSmrg int i; 21223a0898aSmrg char copy[MAXFONTFILENAMELEN]; 21323a0898aSmrg char *fileName; 21423a0898aSmrg FontTablePtr table; 21523a0898aSmrg FontRendererPtr renderer; 21623a0898aSmrg int len; 21723a0898aSmrg FontNameRec name; 21823a0898aSmrg 21923a0898aSmrg table = &dir->nonScalable; 22023a0898aSmrg for (i = 0; i < table->used; i++) { 22123a0898aSmrg if (table->entries[i].type != FONT_ENTRY_BITMAP) 22223a0898aSmrg continue; 22323a0898aSmrg fileName = table->entries[i].u.bitmap.fileName; 22423a0898aSmrg renderer = FontFileMatchRenderer (fileName); 22523a0898aSmrg if (!renderer) 22623a0898aSmrg continue; 22741c30155Smrg 22823a0898aSmrg len = strlen (fileName) - renderer->fileSuffixLen; 22923a0898aSmrg if (len >= sizeof(copy)) 23023a0898aSmrg continue; 23123a0898aSmrg CopyISOLatin1Lowered (copy, fileName, len); 23223a0898aSmrg copy[len] = '\0'; 23323a0898aSmrg name.name = copy; 23423a0898aSmrg name.length = len; 23523a0898aSmrg name.ndashes = FontFileCountDashes (copy, len); 23623a0898aSmrg 23723a0898aSmrg if (!FontFileFindNameInDir(table, &name)) { 23823a0898aSmrg if (!FontFileAddFontAlias (dir, copy, table->entries[i].name.name)) 23923a0898aSmrg return FALSE; 24023a0898aSmrg } 24123a0898aSmrg } 24223a0898aSmrg return TRUE; 24323a0898aSmrg} 24423a0898aSmrg 24523a0898aSmrg/* 24623a0898aSmrg * parse the font.alias file. Format is: 24723a0898aSmrg * 24823a0898aSmrg * alias font-name 24923a0898aSmrg * 25023a0898aSmrg * To imbed white-space in an alias name, enclose it like "font name" 25123a0898aSmrg * in double quotes. \ escapes and character, so 25223a0898aSmrg * "font name \"With Double Quotes\" \\ and \\ back-slashes" 25323a0898aSmrg * works just fine. 25423a0898aSmrg * 25523a0898aSmrg * A line beginning with a ! denotes a newline-terminated comment. 25623a0898aSmrg */ 25723a0898aSmrg 25823a0898aSmrg/* 25923a0898aSmrg * token types 26023a0898aSmrg */ 26123a0898aSmrg 26223a0898aSmrg#define NAME 0 26323a0898aSmrg#define NEWLINE 1 26423a0898aSmrg#define DONE 2 26523a0898aSmrg#define EALLOC 3 26623a0898aSmrg 26723a0898aSmrgstatic int 26823a0898aSmrgReadFontAlias(char *directory, Bool isFile, FontDirectoryPtr *pdir) 26923a0898aSmrg{ 27023a0898aSmrg char alias[MAXFONTNAMELEN]; 27123a0898aSmrg char font_name[MAXFONTNAMELEN]; 27223a0898aSmrg char alias_file[MAXFONTFILENAMELEN]; 273f262af57Smrg int file_fd; 274f262af57Smrg FILE *file = 0; 27523a0898aSmrg FontDirectoryPtr dir; 27623a0898aSmrg int token; 27723a0898aSmrg char *lexToken; 27823a0898aSmrg int status = Successful; 27923a0898aSmrg struct stat statb; 28023a0898aSmrg 28123a0898aSmrg if (strlen(directory) >= sizeof(alias_file)) 28223a0898aSmrg return BadFontPath; 28323a0898aSmrg dir = *pdir; 28423a0898aSmrg strcpy(alias_file, directory); 28523a0898aSmrg if (!isFile) { 28623a0898aSmrg if (strlen(directory) + 1 + sizeof(FontAliasFile) > sizeof(alias_file)) 28723a0898aSmrg return BadFontPath; 28823a0898aSmrg if (directory[strlen(directory) - 1] != '/') 28923a0898aSmrg strcat(alias_file, "/"); 29023a0898aSmrg strcat(alias_file, FontAliasFile); 29123a0898aSmrg } 292f262af57Smrg 293f262af57Smrg#ifndef WIN32 294f262af57Smrg file_fd = open(alias_file, O_RDONLY | O_NOFOLLOW); 295f262af57Smrg if (file_fd >= 0) { 296f262af57Smrg file = fdopen(file_fd, "rt"); 297f262af57Smrg } 298f262af57Smrg#else 29923a0898aSmrg file = fopen(alias_file, "rt"); 300f262af57Smrg#endif 301f262af57Smrg 30223a0898aSmrg if (!file) 30323a0898aSmrg return ((errno == ENOENT) ? Successful : BadFontPath); 30423a0898aSmrg if (!dir) 30523a0898aSmrg *pdir = dir = FontFileMakeDir(directory, 10); 30623a0898aSmrg if (!dir) 30723a0898aSmrg { 30823a0898aSmrg fclose (file); 30923a0898aSmrg return AllocError; 31023a0898aSmrg } 31123a0898aSmrg#ifndef WIN32 31223a0898aSmrg if (fstat (fileno (file), &statb) == -1) 31323a0898aSmrg#else 31423a0898aSmrg if (stat (alias_file, &statb) == -1) 31523a0898aSmrg#endif 31623a0898aSmrg { 31723a0898aSmrg fclose (file); 31823a0898aSmrg return BadFontPath; 31923a0898aSmrg } 32023a0898aSmrg dir->alias_mtime = statb.st_mtime; 32123a0898aSmrg while (status == Successful) { 32223a0898aSmrg token = lexAlias(file, &lexToken); 32323a0898aSmrg switch (token) { 32423a0898aSmrg case NEWLINE: 32523a0898aSmrg break; 32623a0898aSmrg case DONE: 32723a0898aSmrg fclose(file); 32823a0898aSmrg return Successful; 32923a0898aSmrg case EALLOC: 33023a0898aSmrg status = AllocError; 33123a0898aSmrg break; 33223a0898aSmrg case NAME: 33323a0898aSmrg if (strlen(lexToken) >= sizeof(alias)) { 33423a0898aSmrg status = BadFontPath; 33523a0898aSmrg break; 33623a0898aSmrg } 33723a0898aSmrg strcpy(alias, lexToken); 33823a0898aSmrg token = lexAlias(file, &lexToken); 33923a0898aSmrg switch (token) { 34023a0898aSmrg case NEWLINE: 34123a0898aSmrg if (strcmp(alias, "FILE_NAMES_ALIASES")) 34223a0898aSmrg status = BadFontPath; 34323a0898aSmrg else if (!AddFileNameAliases(dir)) 34423a0898aSmrg status = AllocError; 34523a0898aSmrg break; 34623a0898aSmrg case DONE: 34723a0898aSmrg status = BadFontPath; 34823a0898aSmrg break; 34923a0898aSmrg case EALLOC: 35023a0898aSmrg status = AllocError; 35123a0898aSmrg break; 35223a0898aSmrg case NAME: 35323a0898aSmrg if (strlen(lexToken) >= sizeof(font_name)) { 35423a0898aSmrg status = BadFontPath; 35523a0898aSmrg break; 35623a0898aSmrg } 35723a0898aSmrg CopyISOLatin1Lowered(alias, alias, strlen(alias)); 35823a0898aSmrg CopyISOLatin1Lowered(font_name, lexToken, strlen(lexToken)); 35923a0898aSmrg if (!FontFileAddFontAlias (dir, alias, font_name)) 36023a0898aSmrg status = AllocError; 36123a0898aSmrg break; 36223a0898aSmrg } 36323a0898aSmrg } 36423a0898aSmrg } 36523a0898aSmrg fclose(file); 36623a0898aSmrg return status; 36723a0898aSmrg} 36823a0898aSmrg 36923a0898aSmrg#define QUOTE 0 37023a0898aSmrg#define WHITE 1 37123a0898aSmrg#define NORMAL 2 37223a0898aSmrg#define END 3 37323a0898aSmrg#define NL 4 37423a0898aSmrg#define BANG 5 37523a0898aSmrg 37623a0898aSmrgstatic int charClass; 37723a0898aSmrg 37823a0898aSmrgstatic int 37923a0898aSmrglexAlias(FILE *file, char **lexToken) 38023a0898aSmrg{ 38123a0898aSmrg int c; 38223a0898aSmrg char *t; 38323a0898aSmrg enum state { 38423a0898aSmrg Begin, Normal, Quoted, Comment 38523a0898aSmrg } state; 38623a0898aSmrg int count; 38723a0898aSmrg 38823a0898aSmrg static char *tokenBuf = (char *) NULL; 38923a0898aSmrg static int tokenSize = 0; 39023a0898aSmrg 39123a0898aSmrg t = tokenBuf; 39223a0898aSmrg count = 0; 39323a0898aSmrg state = Begin; 39423a0898aSmrg for (;;) { 39523a0898aSmrg if (count == tokenSize) { 39623a0898aSmrg int nsize; 39723a0898aSmrg char *nbuf; 39823a0898aSmrg 3997673729aSmrg if (tokenSize >= (INT_MAX >> 2)) 4007673729aSmrg /* Stop before we overflow */ 4017673729aSmrg return EALLOC; 40223a0898aSmrg nsize = tokenSize ? (tokenSize << 1) : 64; 4037f7f5e4eSmrg nbuf = realloc(tokenBuf, nsize); 40423a0898aSmrg if (!nbuf) 40523a0898aSmrg return EALLOC; 40623a0898aSmrg tokenBuf = nbuf; 40723a0898aSmrg tokenSize = nsize; 40823a0898aSmrg t = tokenBuf + count; 40923a0898aSmrg } 41023a0898aSmrg c = lexc(file); 41123a0898aSmrg switch (charClass) { 41223a0898aSmrg case QUOTE: 41323a0898aSmrg switch (state) { 41423a0898aSmrg case Begin: 41523a0898aSmrg case Normal: 41623a0898aSmrg state = Quoted; 41723a0898aSmrg break; 41823a0898aSmrg case Quoted: 41923a0898aSmrg state = Normal; 42023a0898aSmrg break; 42123a0898aSmrg case Comment: 42223a0898aSmrg break; 42323a0898aSmrg } 42423a0898aSmrg break; 42523a0898aSmrg case WHITE: 42623a0898aSmrg switch (state) { 42723a0898aSmrg case Begin: 42823a0898aSmrg case Comment: 42923a0898aSmrg continue; 43023a0898aSmrg case Normal: 43123a0898aSmrg *t = '\0'; 43223a0898aSmrg *lexToken = tokenBuf; 43323a0898aSmrg return NAME; 43423a0898aSmrg case Quoted: 43523a0898aSmrg break; 43623a0898aSmrg } 43723a0898aSmrg /* fall through */ 43823a0898aSmrg case NORMAL: 43923a0898aSmrg switch (state) { 44023a0898aSmrg case Begin: 44123a0898aSmrg state = Normal; 44223a0898aSmrg break; 44323a0898aSmrg case Comment: 44423a0898aSmrg continue; 44523a0898aSmrg default: 44623a0898aSmrg break; 44723a0898aSmrg } 44823a0898aSmrg *t++ = c; 44923a0898aSmrg ++count; 45023a0898aSmrg break; 45123a0898aSmrg case END: 45223a0898aSmrg case NL: 45323a0898aSmrg switch (state) { 45423a0898aSmrg case Begin: 45523a0898aSmrg case Comment: 45623a0898aSmrg *lexToken = (char *) NULL; 45723a0898aSmrg return charClass == END ? DONE : NEWLINE; 45823a0898aSmrg default: 45923a0898aSmrg *t = '\0'; 46023a0898aSmrg *lexToken = tokenBuf; 46123a0898aSmrg ungetc(c, file); 46223a0898aSmrg return NAME; 46323a0898aSmrg } 46423a0898aSmrg break; 46523a0898aSmrg case BANG: 46623a0898aSmrg switch (state) { 46723a0898aSmrg case Begin: 46823a0898aSmrg state = Comment; 46923a0898aSmrg break; 47023a0898aSmrg case Comment: 47123a0898aSmrg break; 47223a0898aSmrg default: 47323a0898aSmrg *t++ = c; 47423a0898aSmrg ++count; 47523a0898aSmrg } 47623a0898aSmrg break; 47723a0898aSmrg } 47823a0898aSmrg } 47923a0898aSmrg} 48023a0898aSmrg 48123a0898aSmrgstatic int 48223a0898aSmrglexc(FILE *file) 48323a0898aSmrg{ 48423a0898aSmrg int c; 48523a0898aSmrg 48623a0898aSmrg c = getc(file); 48723a0898aSmrg switch (c) { 48823a0898aSmrg case EOF: 48923a0898aSmrg charClass = END; 49023a0898aSmrg break; 49123a0898aSmrg case '\\': 49223a0898aSmrg c = getc(file); 49323a0898aSmrg if (c == EOF) 49423a0898aSmrg charClass = END; 49523a0898aSmrg else 49623a0898aSmrg charClass = NORMAL; 49723a0898aSmrg break; 49823a0898aSmrg case '"': 49923a0898aSmrg charClass = QUOTE; 50023a0898aSmrg break; 50123a0898aSmrg case ' ': 50223a0898aSmrg case '\t': 50323a0898aSmrg charClass = WHITE; 50423a0898aSmrg break; 50523a0898aSmrg case '\r': 50623a0898aSmrg case '\n': 50723a0898aSmrg charClass = NL; 50823a0898aSmrg break; 50923a0898aSmrg case '!': 51023a0898aSmrg charClass = BANG; 51123a0898aSmrg break; 51223a0898aSmrg default: 51323a0898aSmrg charClass = NORMAL; 51423a0898aSmrg break; 51523a0898aSmrg } 51623a0898aSmrg return c; 51723a0898aSmrg} 518