dirfile.c revision 0b332824
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>
4423a0898aSmrg#include <errno.h>
459d21a897Sspz#include <limits.h>
4623a0898aSmrg
4723a0898aSmrgstatic Bool AddFileNameAliases ( FontDirectoryPtr dir );
4823a0898aSmrgstatic int ReadFontAlias ( char *directory, Bool isFile,
4923a0898aSmrg			   FontDirectoryPtr *pdir );
5023a0898aSmrgstatic int lexAlias ( FILE *file, char **lexToken );
5123a0898aSmrgstatic int lexc ( FILE *file );
5223a0898aSmrg
530b332824Ssnj#pragma GCC diagnostic ignored "-Wformat-nonliteral"
540b332824Ssnj
5523a0898aSmrgint
560b332824SsnjFontFileReadDirectory (const char *directory, FontDirectoryPtr *pdir)
5723a0898aSmrg{
5823a0898aSmrg    char        file_name[MAXFONTFILENAMELEN];
5923a0898aSmrg    char        font_name[MAXFONTNAMELEN];
6023a0898aSmrg    char        dir_file[MAXFONTFILENAMELEN];
6123a0898aSmrg    char	dir_path[MAXFONTFILENAMELEN];
6223a0898aSmrg    char	*ptr;
6323a0898aSmrg    FILE       *file;
6423a0898aSmrg    int         count,
6523a0898aSmrg                num_fonts,
6623a0898aSmrg                status;
6723a0898aSmrg    struct stat	statb;
6823a0898aSmrg    static char format[24] = "";
697f7f5e4eSmrg#if defined(WIN32)
707f7f5e4eSmrg    int i;
717f7f5e4eSmrg#endif
7223a0898aSmrg
7323a0898aSmrg    FontDirectoryPtr	dir = NullFontDirectory;
7423a0898aSmrg
7523a0898aSmrg    if (strlen(directory) + 1 + sizeof(FontDirFile) > sizeof(dir_file))
7623a0898aSmrg	return BadFontPath;
7723a0898aSmrg
7823a0898aSmrg    /* Check for font directory attributes */
797f7f5e4eSmrg#if !defined(WIN32)
8023a0898aSmrg    if ((ptr = strchr(directory, ':'))) {
8123a0898aSmrg#else
8223a0898aSmrg    /* OS/2 and WIN32 path might start with a drive letter, don't clip this */
8323a0898aSmrg    if ((ptr = strchr(directory+2, ':'))) {
8423a0898aSmrg#endif
8523a0898aSmrg	strncpy(dir_path, directory, ptr - directory);
8623a0898aSmrg	dir_path[ptr - directory] = '\0';
8723a0898aSmrg    } else {
8823a0898aSmrg	strcpy(dir_path, directory);
8923a0898aSmrg    }
9023a0898aSmrg    strcpy(dir_file, dir_path);
9123a0898aSmrg    if (dir_file[strlen(dir_file) - 1] != '/')
9223a0898aSmrg	strcat(dir_file, "/");
9323a0898aSmrg    strcat(dir_file, FontDirFile);
9423a0898aSmrg    file = fopen(dir_file, "rt");
9523a0898aSmrg    if (file) {
9641c30155Smrg#ifndef WIN32
9723a0898aSmrg	if (fstat (fileno(file), &statb) == -1)
9823a0898aSmrg#else
9923a0898aSmrg	if (stat (dir_file, &statb) == -1)
10023a0898aSmrg#endif
10123a0898aSmrg        {
10223a0898aSmrg            fclose(file);
10323a0898aSmrg	    return BadFontPath;
10423a0898aSmrg        }
10523a0898aSmrg	count = fscanf(file, "%d\n", &num_fonts);
10623a0898aSmrg	if ((count == EOF) || (count != 1)) {
10723a0898aSmrg	    fclose(file);
10823a0898aSmrg	    return BadFontPath;
10923a0898aSmrg	}
11023a0898aSmrg	dir = FontFileMakeDir(directory, num_fonts);
11123a0898aSmrg	if (dir == NULL) {
11223a0898aSmrg	    fclose(file);
11323a0898aSmrg	    return BadFontPath;
11423a0898aSmrg	}
11523a0898aSmrg	dir->dir_mtime = statb.st_mtime;
11623a0898aSmrg	if (format[0] == '\0')
11723a0898aSmrg	    sprintf(format, "%%%ds %%%d[^\n]\n",
11823a0898aSmrg		MAXFONTFILENAMELEN-1, MAXFONTNAMELEN-1);
11923a0898aSmrg
12023a0898aSmrg	while ((count = fscanf(file, format, file_name, font_name)) != EOF) {
1217f7f5e4eSmrg#if defined(WIN32)
12223a0898aSmrg	    /* strip any existing trailing CR */
12323a0898aSmrg	    for (i=0; i<strlen(font_name); i++) {
12423a0898aSmrg		if (font_name[i]=='\r') font_name[i] = '\0';
12523a0898aSmrg	    }
12623a0898aSmrg#endif
12723a0898aSmrg	    if (count != 2) {
12823a0898aSmrg		FontFileFreeDir (dir);
12923a0898aSmrg		fclose(file);
13023a0898aSmrg		return BadFontPath;
13123a0898aSmrg	    }
13223a0898aSmrg
13323a0898aSmrg	    /*
13423a0898aSmrg	     * We blindly try to load all the font files specified.
13523a0898aSmrg	     * In theory, we might want to warn that some of the fonts
13623a0898aSmrg	     * couldn't be loaded.
13723a0898aSmrg	     */
13823a0898aSmrg	    FontFileAddFontFile (dir, font_name, file_name);
13923a0898aSmrg	}
14023a0898aSmrg	fclose(file);
14141c30155Smrg
14223a0898aSmrg    } else if (errno != ENOENT) {
14323a0898aSmrg	return BadFontPath;
14423a0898aSmrg    }
14523a0898aSmrg    status = ReadFontAlias(dir_path, FALSE, &dir);
14623a0898aSmrg    if (status != Successful) {
14723a0898aSmrg	if (dir)
14823a0898aSmrg	    FontFileFreeDir (dir);
14923a0898aSmrg	return status;
15023a0898aSmrg    }
15123a0898aSmrg    if (!dir)
15223a0898aSmrg	return BadFontPath;
15323a0898aSmrg
15423a0898aSmrg    FontFileSortDir(dir);
15523a0898aSmrg
15623a0898aSmrg    *pdir = dir;
15723a0898aSmrg    return Successful;
15823a0898aSmrg}
15923a0898aSmrg
16023a0898aSmrgBool
16123a0898aSmrgFontFileDirectoryChanged(FontDirectoryPtr dir)
16223a0898aSmrg{
16323a0898aSmrg    char	dir_file[MAXFONTFILENAMELEN];
16423a0898aSmrg    struct stat	statb;
16523a0898aSmrg
16623a0898aSmrg    if (strlen(dir->directory) + sizeof(FontDirFile) > sizeof(dir_file))
16723a0898aSmrg	return FALSE;
16823a0898aSmrg
16923a0898aSmrg    strcpy (dir_file, dir->directory);
17023a0898aSmrg    strcat (dir_file, FontDirFile);
17123a0898aSmrg    if (stat (dir_file, &statb) == -1)
17223a0898aSmrg    {
17323a0898aSmrg	if (errno != ENOENT || dir->dir_mtime != 0)
17423a0898aSmrg	    return TRUE;
17523a0898aSmrg	return FALSE;		/* doesn't exist and never did: no change */
17623a0898aSmrg    }
17723a0898aSmrg    if (dir->dir_mtime != statb.st_mtime)
17823a0898aSmrg	return TRUE;
1797f7f5e4eSmrg
1807f7f5e4eSmrg    if ((strlen(dir->directory) + sizeof(FontAliasFile)) > sizeof(dir_file))
1817f7f5e4eSmrg	return FALSE;
18223a0898aSmrg    strcpy (dir_file, dir->directory);
18323a0898aSmrg    strcat (dir_file, FontAliasFile);
18423a0898aSmrg    if (stat (dir_file, &statb) == -1)
18523a0898aSmrg    {
18623a0898aSmrg	if (errno != ENOENT || dir->alias_mtime != 0)
18723a0898aSmrg	    return TRUE;
18823a0898aSmrg	return FALSE;		/* doesn't exist and never did: no change */
18923a0898aSmrg    }
19023a0898aSmrg    if (dir->alias_mtime != statb.st_mtime)
19123a0898aSmrg	return TRUE;
19223a0898aSmrg    return FALSE;
19323a0898aSmrg}
19441c30155Smrg
19523a0898aSmrg/*
19623a0898aSmrg * Make each of the file names an automatic alias for each of the files.
19723a0898aSmrg */
19823a0898aSmrg
19923a0898aSmrgstatic Bool
20023a0898aSmrgAddFileNameAliases(FontDirectoryPtr dir)
20123a0898aSmrg{
20223a0898aSmrg    int		    i;
20323a0898aSmrg    char	    copy[MAXFONTFILENAMELEN];
20423a0898aSmrg    char	    *fileName;
20523a0898aSmrg    FontTablePtr    table;
20623a0898aSmrg    FontRendererPtr renderer;
20723a0898aSmrg    int		    len;
20823a0898aSmrg    FontNameRec	    name;
20923a0898aSmrg
21023a0898aSmrg    table = &dir->nonScalable;
21123a0898aSmrg    for (i = 0; i < table->used; i++) {
21223a0898aSmrg	if (table->entries[i].type != FONT_ENTRY_BITMAP)
21323a0898aSmrg	    continue;
21423a0898aSmrg	fileName = table->entries[i].u.bitmap.fileName;
21523a0898aSmrg	renderer = FontFileMatchRenderer (fileName);
21623a0898aSmrg	if (!renderer)
21723a0898aSmrg	    continue;
21841c30155Smrg
21923a0898aSmrg	len = strlen (fileName) - renderer->fileSuffixLen;
22023a0898aSmrg	if (len >= sizeof(copy))
22123a0898aSmrg	    continue;
22223a0898aSmrg	CopyISOLatin1Lowered (copy, fileName, len);
22323a0898aSmrg	copy[len] = '\0';
22423a0898aSmrg	name.name = copy;
22523a0898aSmrg	name.length = len;
22623a0898aSmrg	name.ndashes = FontFileCountDashes (copy, len);
22723a0898aSmrg
22823a0898aSmrg	if (!FontFileFindNameInDir(table, &name)) {
22923a0898aSmrg	    if (!FontFileAddFontAlias (dir, copy, table->entries[i].name.name))
23023a0898aSmrg		return FALSE;
23123a0898aSmrg	}
23223a0898aSmrg    }
23323a0898aSmrg    return TRUE;
23423a0898aSmrg}
23523a0898aSmrg
23623a0898aSmrg/*
23723a0898aSmrg * parse the font.alias file.  Format is:
23823a0898aSmrg *
23923a0898aSmrg * alias font-name
24023a0898aSmrg *
24123a0898aSmrg * To imbed white-space in an alias name, enclose it like "font name"
24223a0898aSmrg * in double quotes.  \ escapes and character, so
24323a0898aSmrg * "font name \"With Double Quotes\" \\ and \\ back-slashes"
24423a0898aSmrg * works just fine.
24523a0898aSmrg *
24623a0898aSmrg * A line beginning with a ! denotes a newline-terminated comment.
24723a0898aSmrg */
24823a0898aSmrg
24923a0898aSmrg/*
25023a0898aSmrg * token types
25123a0898aSmrg */
25223a0898aSmrg
25323a0898aSmrg#define NAME		0
25423a0898aSmrg#define NEWLINE		1
25523a0898aSmrg#define DONE		2
25623a0898aSmrg#define EALLOC		3
25723a0898aSmrg
25823a0898aSmrgstatic int
25923a0898aSmrgReadFontAlias(char *directory, Bool isFile, FontDirectoryPtr *pdir)
26023a0898aSmrg{
26123a0898aSmrg    char		alias[MAXFONTNAMELEN];
26223a0898aSmrg    char		font_name[MAXFONTNAMELEN];
26323a0898aSmrg    char		alias_file[MAXFONTFILENAMELEN];
26423a0898aSmrg    FILE		*file;
26523a0898aSmrg    FontDirectoryPtr	dir;
26623a0898aSmrg    int			token;
26723a0898aSmrg    char		*lexToken;
26823a0898aSmrg    int			status = Successful;
26923a0898aSmrg    struct stat		statb;
27023a0898aSmrg
27123a0898aSmrg    if (strlen(directory) >= sizeof(alias_file))
27223a0898aSmrg	return BadFontPath;
27323a0898aSmrg    dir = *pdir;
27423a0898aSmrg    strcpy(alias_file, directory);
27523a0898aSmrg    if (!isFile) {
27623a0898aSmrg	if (strlen(directory) + 1 + sizeof(FontAliasFile) > sizeof(alias_file))
27723a0898aSmrg	    return BadFontPath;
27823a0898aSmrg	if (directory[strlen(directory) - 1] != '/')
27923a0898aSmrg	    strcat(alias_file, "/");
28023a0898aSmrg	strcat(alias_file, FontAliasFile);
28123a0898aSmrg    }
28223a0898aSmrg    file = fopen(alias_file, "rt");
28323a0898aSmrg    if (!file)
28423a0898aSmrg	return ((errno == ENOENT) ? Successful : BadFontPath);
28523a0898aSmrg    if (!dir)
28623a0898aSmrg	*pdir = dir = FontFileMakeDir(directory, 10);
28723a0898aSmrg    if (!dir)
28823a0898aSmrg    {
28923a0898aSmrg	fclose (file);
29023a0898aSmrg	return AllocError;
29123a0898aSmrg    }
29223a0898aSmrg#ifndef WIN32
29323a0898aSmrg    if (fstat (fileno (file), &statb) == -1)
29423a0898aSmrg#else
29523a0898aSmrg    if (stat (alias_file, &statb) == -1)
29623a0898aSmrg#endif
29723a0898aSmrg    {
29823a0898aSmrg	fclose (file);
29923a0898aSmrg	return BadFontPath;
30023a0898aSmrg    }
30123a0898aSmrg    dir->alias_mtime = statb.st_mtime;
30223a0898aSmrg    while (status == Successful) {
30323a0898aSmrg	token = lexAlias(file, &lexToken);
30423a0898aSmrg	switch (token) {
30523a0898aSmrg	case NEWLINE:
30623a0898aSmrg	    break;
30723a0898aSmrg	case DONE:
30823a0898aSmrg	    fclose(file);
30923a0898aSmrg	    return Successful;
31023a0898aSmrg	case EALLOC:
31123a0898aSmrg	    status = AllocError;
31223a0898aSmrg	    break;
31323a0898aSmrg	case NAME:
31423a0898aSmrg	    if (strlen(lexToken) >= sizeof(alias)) {
31523a0898aSmrg		status = BadFontPath;
31623a0898aSmrg		break;
31723a0898aSmrg	    }
31823a0898aSmrg	    strcpy(alias, lexToken);
31923a0898aSmrg	    token = lexAlias(file, &lexToken);
32023a0898aSmrg	    switch (token) {
32123a0898aSmrg	    case NEWLINE:
32223a0898aSmrg		if (strcmp(alias, "FILE_NAMES_ALIASES"))
32323a0898aSmrg		    status = BadFontPath;
32423a0898aSmrg		else if (!AddFileNameAliases(dir))
32523a0898aSmrg		    status = AllocError;
32623a0898aSmrg		break;
32723a0898aSmrg	    case DONE:
32823a0898aSmrg		status = BadFontPath;
32923a0898aSmrg		break;
33023a0898aSmrg	    case EALLOC:
33123a0898aSmrg		status = AllocError;
33223a0898aSmrg		break;
33323a0898aSmrg	    case NAME:
33423a0898aSmrg		if (strlen(lexToken) >= sizeof(font_name)) {
33523a0898aSmrg		    status = BadFontPath;
33623a0898aSmrg		    break;
33723a0898aSmrg		}
33823a0898aSmrg		CopyISOLatin1Lowered(alias, alias, strlen(alias));
33923a0898aSmrg		CopyISOLatin1Lowered(font_name, lexToken, strlen(lexToken));
34023a0898aSmrg		if (!FontFileAddFontAlias (dir, alias, font_name))
34123a0898aSmrg		    status = AllocError;
34223a0898aSmrg		break;
34323a0898aSmrg	    }
34423a0898aSmrg	}
34523a0898aSmrg    }
34623a0898aSmrg    fclose(file);
34723a0898aSmrg    return status;
34823a0898aSmrg}
34923a0898aSmrg
35023a0898aSmrg#define QUOTE		0
35123a0898aSmrg#define WHITE		1
35223a0898aSmrg#define NORMAL		2
35323a0898aSmrg#define END		3
35423a0898aSmrg#define NL		4
35523a0898aSmrg#define BANG		5
35623a0898aSmrg
35723a0898aSmrgstatic int  charClass;
35823a0898aSmrg
35923a0898aSmrgstatic int
36023a0898aSmrglexAlias(FILE *file, char **lexToken)
36123a0898aSmrg{
36223a0898aSmrg    int         c;
36323a0898aSmrg    char       *t;
36423a0898aSmrg    enum state {
36523a0898aSmrg	Begin, Normal, Quoted, Comment
36623a0898aSmrg    }           state;
36723a0898aSmrg    int         count;
36823a0898aSmrg
36923a0898aSmrg    static char *tokenBuf = (char *) NULL;
37023a0898aSmrg    static int  tokenSize = 0;
37123a0898aSmrg
37223a0898aSmrg    t = tokenBuf;
37323a0898aSmrg    count = 0;
37423a0898aSmrg    state = Begin;
37523a0898aSmrg    for (;;) {
37623a0898aSmrg	if (count == tokenSize) {
37723a0898aSmrg	    int         nsize;
37823a0898aSmrg	    char       *nbuf;
37923a0898aSmrg
3807673729aSmrg	    if (tokenSize >= (INT_MAX >> 2))
3817673729aSmrg		/* Stop before we overflow */
3827673729aSmrg		return EALLOC;
38323a0898aSmrg	    nsize = tokenSize ? (tokenSize << 1) : 64;
3847f7f5e4eSmrg	    nbuf = realloc(tokenBuf, nsize);
38523a0898aSmrg	    if (!nbuf)
38623a0898aSmrg		return EALLOC;
38723a0898aSmrg	    tokenBuf = nbuf;
38823a0898aSmrg	    tokenSize = nsize;
38923a0898aSmrg	    t = tokenBuf + count;
39023a0898aSmrg	}
39123a0898aSmrg	c = lexc(file);
39223a0898aSmrg	switch (charClass) {
39323a0898aSmrg	case QUOTE:
39423a0898aSmrg	    switch (state) {
39523a0898aSmrg	    case Begin:
39623a0898aSmrg	    case Normal:
39723a0898aSmrg		state = Quoted;
39823a0898aSmrg		break;
39923a0898aSmrg	    case Quoted:
40023a0898aSmrg		state = Normal;
40123a0898aSmrg		break;
40223a0898aSmrg	    case Comment:
40323a0898aSmrg		break;
40423a0898aSmrg	    }
40523a0898aSmrg	    break;
40623a0898aSmrg	case WHITE:
40723a0898aSmrg	    switch (state) {
40823a0898aSmrg	    case Begin:
40923a0898aSmrg	    case Comment:
41023a0898aSmrg		continue;
41123a0898aSmrg	    case Normal:
41223a0898aSmrg		*t = '\0';
41323a0898aSmrg		*lexToken = tokenBuf;
41423a0898aSmrg		return NAME;
41523a0898aSmrg	    case Quoted:
41623a0898aSmrg		break;
41723a0898aSmrg	    }
41823a0898aSmrg	    /* fall through */
41923a0898aSmrg	case NORMAL:
42023a0898aSmrg	    switch (state) {
42123a0898aSmrg	    case Begin:
42223a0898aSmrg		state = Normal;
42323a0898aSmrg		break;
42423a0898aSmrg	    case Comment:
42523a0898aSmrg		continue;
42623a0898aSmrg	    default:
42723a0898aSmrg		break;
42823a0898aSmrg	    }
42923a0898aSmrg	    *t++ = c;
43023a0898aSmrg	    ++count;
43123a0898aSmrg	    break;
43223a0898aSmrg	case END:
43323a0898aSmrg	case NL:
43423a0898aSmrg	    switch (state) {
43523a0898aSmrg	    case Begin:
43623a0898aSmrg	    case Comment:
43723a0898aSmrg		*lexToken = (char *) NULL;
43823a0898aSmrg		return charClass == END ? DONE : NEWLINE;
43923a0898aSmrg	    default:
44023a0898aSmrg		*t = '\0';
44123a0898aSmrg		*lexToken = tokenBuf;
44223a0898aSmrg		ungetc(c, file);
44323a0898aSmrg		return NAME;
44423a0898aSmrg	    }
44523a0898aSmrg	    break;
44623a0898aSmrg	case BANG:
44723a0898aSmrg	    switch (state) {
44823a0898aSmrg	    case Begin:
44923a0898aSmrg		state = Comment;
45023a0898aSmrg		break;
45123a0898aSmrg            case Comment:
45223a0898aSmrg		break;
45323a0898aSmrg            default:
45423a0898aSmrg		*t++ = c;
45523a0898aSmrg		++count;
45623a0898aSmrg	    }
45723a0898aSmrg	    break;
45823a0898aSmrg	}
45923a0898aSmrg    }
46023a0898aSmrg}
46123a0898aSmrg
46223a0898aSmrgstatic int
46323a0898aSmrglexc(FILE *file)
46423a0898aSmrg{
46523a0898aSmrg    int         c;
46623a0898aSmrg
46723a0898aSmrg    c = getc(file);
46823a0898aSmrg    switch (c) {
46923a0898aSmrg    case EOF:
47023a0898aSmrg	charClass = END;
47123a0898aSmrg	break;
47223a0898aSmrg    case '\\':
47323a0898aSmrg	c = getc(file);
47423a0898aSmrg	if (c == EOF)
47523a0898aSmrg	    charClass = END;
47623a0898aSmrg	else
47723a0898aSmrg	    charClass = NORMAL;
47823a0898aSmrg	break;
47923a0898aSmrg    case '"':
48023a0898aSmrg	charClass = QUOTE;
48123a0898aSmrg	break;
48223a0898aSmrg    case ' ':
48323a0898aSmrg    case '\t':
48423a0898aSmrg	charClass = WHITE;
48523a0898aSmrg	break;
48623a0898aSmrg    case '\r':
48723a0898aSmrg    case '\n':
48823a0898aSmrg	charClass = NL;
48923a0898aSmrg	break;
49023a0898aSmrg    case '!':
49123a0898aSmrg	charClass = BANG;
49223a0898aSmrg	break;
49323a0898aSmrg    default:
49423a0898aSmrg	charClass = NORMAL;
49523a0898aSmrg	break;
49623a0898aSmrg    }
49723a0898aSmrg    return c;
49823a0898aSmrg}
499