#include <string.h>
#include <stdio.h>
#ifdef SCO325
#include <strings.h>
#endif
#ifndef FONTENC_NO_LIBFONT
#include "fntfilio.h"
#include "fntfilst.h"
#else
#include <stdlib.h>
#define xalloc(n) malloc(n)
#define xrealloc(p, n) realloc(p, n)
#define xfree(p) free(p)
#include "zlib.h"
typedef gzFile FontFilePtr;
#define FontFileGetc(f) gzgetc(f)
#define FontFileOpen(filename) gzopen(filename, "rb")
#define FontFileClose(f) gzclose(f)
#define MAXFONTFILENAMELEN 1024
#define MAXFONTNAMELEN 1024
#endif
#include "fontenc.h"
#include "fontencI.h"
#define MAXALIASES 20
#define EOF_TOKEN -1
#define ERROR_TOKEN -2
#define EOL_TOKEN 0
#define NUMBER_TOKEN 1
#define KEYWORD_TOKEN 2
#define EOF_LINE -1
#define ERROR_LINE -2
#define STARTENCODING_LINE 1
#define STARTMAPPING_LINE 2
#define ENDMAPPING_LINE 3
#define CODE_LINE 4
#define CODE_RANGE_LINE 5
#define CODE_UNDEFINE_LINE 6
#define NAME_LINE 7
#define SIZE_LINE 8
#define ALIAS_LINE 9
#define FIRSTINDEX_LINE 10
#define MAXKEYWORDLEN 100
static long number_value;
static char keyword_value[MAXKEYWORDLEN+1];
static long value1, value2, value3;
static void
skipEndOfLine(FontFilePtr f, int c)
{
if(c == 0)
c = FontFileGetc(f);
for(;;)
if(c <= 0 || c == '\n')
return;
else
c = FontFileGetc(f);
}
static unsigned
getnum(FontFilePtr f, int c, int *cp)
{
unsigned n = 0;
int base = 10;
if(c == '0') {
c = FontFileGetc(f);
base = 8;
if(c == 'x' || c == 'X') {
base = 16;
c = FontFileGetc(f);
}
}
for(;;) {
if ('0' <= c && c <= '9') {
n *= base; n += c - '0';
} else if('a' <= c && c <= 'f') {
n *= base; n += c - 'a' + 10;
} else if('A' <=c && c <= 'F') {
n *= base; n += c - 'A' + 10;
} else
break;
c = FontFileGetc(f);
}
*cp = c; return n;
}
static int
endOfLine(FontFilePtr f, int c)
{
if(c == 0)
c = FontFileGetc(f);
for(;;) {
if(c <= 0 || c == '\n')
return 1;
else if(c == '#') {
skipEndOfLine(f,c);
return 1;
}
else if(c == ' ' || c == '\t') {
skipEndOfLine(f,c);
return 0;
}
c = FontFileGetc(f);
}
}
static int
gettoken(FontFilePtr f, int c, int *cp)
{
char *p;
if(c <= 0)
c = FontFileGetc(f);
if(c <= 0) {
return EOF_TOKEN;
}
while(c == ' ' || c == '\t')
c = FontFileGetc(f);
if(c=='\n') {
return EOL_TOKEN;
} else if(c == '#') {
skipEndOfLine(f,c);
return EOL_TOKEN;
} else if(c >= '0' && c <= '9') {
number_value = getnum(f,c,cp);
return NUMBER_TOKEN;
} else if((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == '/' || c == '_' || c == '-' || c == '.') {
p = keyword_value;
*p++ = c;
while(p-keyword_value < MAXKEYWORDLEN) {
c = FontFileGetc(f);
if(c <= ' ' || c > '~' || c == '#')
break;
*p++ = c;
}
*cp = c;
*p = '\0';
return KEYWORD_TOKEN;
} else {
*cp = c;
return ERROR_TOKEN;
}
}
static int
getnextline(FontFilePtr f)
{
int c, token;
c = FontFileGetc(f);
if(c <= 0)
return EOF_LINE;
again:
token=gettoken(f,c,&c);
switch(token) {
case EOF_TOKEN:
return EOF_LINE;
case EOL_TOKEN:
c = FontFileGetc(f);
goto again;
case NUMBER_TOKEN:
value1 = number_value;
token = gettoken(f,c,&c);
switch(token) {
case NUMBER_TOKEN:
value2 = number_value;
token = gettoken(f,c,&c);
switch(token) {
case NUMBER_TOKEN:
value3 = number_value;
return CODE_RANGE_LINE;
case EOL_TOKEN:
return CODE_LINE;
default:
skipEndOfLine(f,c);
return ERROR_LINE;
}
case KEYWORD_TOKEN:
if(!endOfLine(f,c))
return ERROR_LINE;
else
return NAME_LINE;
default:
skipEndOfLine(f,c);
return ERROR_LINE;
}
case KEYWORD_TOKEN:
if(!strcasecmp(keyword_value, "STARTENCODING")) {
token = gettoken(f,c,&c);
if(token == KEYWORD_TOKEN) {
if(endOfLine(f,c))
return STARTENCODING_LINE;
else
return ERROR_LINE;
} else {
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else if(!strcasecmp(keyword_value, "ALIAS")) {
token = gettoken(f,c,&c);
if(token == KEYWORD_TOKEN) {
if(endOfLine(f,c))
return ALIAS_LINE;
else
return ERROR_LINE;
} else {
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else if(!strcasecmp(keyword_value, "SIZE")) {
token = gettoken(f,c,&c);
if(token == NUMBER_TOKEN) {
value1 = number_value;
token = gettoken(f,c,&c);
switch(token) {
case NUMBER_TOKEN:
value2 = number_value;
return SIZE_LINE;
case EOL_TOKEN:
value2=0;
return SIZE_LINE;
default:
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else {
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else if(!strcasecmp(keyword_value, "FIRSTINDEX")) {
token = gettoken(f,c,&c);
if(token == NUMBER_TOKEN) {
value1 = number_value;
token = gettoken(f,c,&c);
switch(token) {
case NUMBER_TOKEN:
value2 = number_value;
return FIRSTINDEX_LINE;
case EOL_TOKEN:
value2 = 0;
return FIRSTINDEX_LINE;
default:
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else {
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else if(!strcasecmp(keyword_value, "STARTMAPPING")) {
keyword_value[0] = 0;
value1 = 0; value1 = 0;
token = gettoken(f,c,&c);
if(token != KEYWORD_TOKEN) {
skipEndOfLine(f, c);
return ERROR_LINE;
}
token = gettoken(f,c,&c);
if(token == NUMBER_TOKEN) {
value1 = number_value;
} else if(token == EOL_TOKEN) {
return STARTMAPPING_LINE;
} else {
skipEndOfLine(f, c);
return ERROR_LINE;
}
token = gettoken(f,c,&c);
if(token == NUMBER_TOKEN) {
value2 = number_value;
} else if(token == EOL_TOKEN) {
return STARTMAPPING_LINE;
} else {
skipEndOfLine(f, c);
return ERROR_LINE;
}
if(!endOfLine(f,c))
return ERROR_LINE;
else {
return STARTMAPPING_LINE;
}
} else if(!strcasecmp(keyword_value, "UNDEFINE")) {
token = gettoken(f,c,&c);
if(token != NUMBER_TOKEN) {
skipEndOfLine(f,c);
return ERROR_LINE;
}
value1 = number_value;
token = gettoken(f,c,&c);
if(token == EOL_TOKEN) {
value2 = value1;
return CODE_UNDEFINE_LINE;
} else if(token == NUMBER_TOKEN) {
value2 = number_value;
if(endOfLine(f,c)) {
return CODE_UNDEFINE_LINE;
} else
return ERROR_LINE;
} else {
skipEndOfLine(f,c);
return ERROR_LINE;
}
} else if(!strcasecmp(keyword_value, "ENDENCODING")) {
if(endOfLine(f,c))
return EOF_LINE;
else
return ERROR_LINE;
} else if(!strcasecmp(keyword_value, "ENDMAPPING")) {
if(endOfLine(f,c))
return ENDMAPPING_LINE;
else
return ERROR_LINE;
} else {
skipEndOfLine(f,c);
return ERROR_LINE;
}
default:
return ERROR_LINE;
}
}
static void
install_mapping(FontEncPtr encoding, FontMapPtr mapping)
{
FontMapPtr m;
if(encoding->mappings == NULL)
encoding->mappings = mapping;
else {
m = encoding->mappings;
while(m->next != NULL)
m = m->next;
m->next = mapping;
}
mapping->next = NULL;
mapping->encoding = encoding;
}
static int
setCode(unsigned from, unsigned to, unsigned row_size,
unsigned *first, unsigned *last,
unsigned *encsize, unsigned short **enc)
{
unsigned index, i;
unsigned short *newenc;
if(from>0xFFFF)
return 0;
if(row_size==0)
index=from;
else {
if((value1 & 0xFF) >= row_size)
return 0;
index = (from>>8) * row_size + (from&0xFF);
}
if(index == to && (index < *first || index > *last))
return 0;
if(*encsize == 0) {
*encsize = (index < 256) ? 256 : 0x10000;
*enc = (unsigned short*)xalloc((*encsize) * sizeof(unsigned short));
if(*enc == NULL) {
*encsize = 0;
return 1;
}
} else if(*encsize <= index) {
*encsize = 0x10000;
if((newenc = (unsigned short*)xrealloc(enc, *encsize))==NULL)
return 1;
*enc = newenc;
}
if(*first > *last) {
*first = *last = index;
}
if(index < *first) {
for(i = index; i < *first; i++)
(*enc)[i] = i;
*first = index;
}
if(index > *last) {
for(i = *last + 1; i <= index; i++)
(*enc)[i] = i;
*last = index;
}
(*enc)[index] = to;
return 0;
}
static FontEncPtr
parseEncodingFile(FontFilePtr f, int headerOnly)
{
int line;
unsigned short *enc=NULL;
char **nam = NULL, **newnam;
unsigned i, first = 0xFFFF, last=0, encsize=0, namsize=0;
FontEncPtr encoding = NULL;
FontMapPtr mapping = NULL;
FontEncSimpleMapPtr sm;
FontEncSimpleNamePtr sn;
char *aliases[MAXALIASES];
int numaliases=0;
#if 0
no_encoding:
#endif
line = getnextline(f);
switch(line) {
case EOF_LINE:
goto error;
case STARTENCODING_LINE:
encoding = (FontEncPtr)xalloc(sizeof(FontEncRec));
if(encoding == NULL)
goto error;
encoding->name = (char*)xalloc(strlen(keyword_value)+1);
if(encoding->name == NULL)
goto error;
strcpy(encoding->name, keyword_value);
encoding->size = 256;
encoding->row_size = 0;
encoding->mappings = NULL;
encoding->next = NULL;
encoding->first = encoding->first_col=0;
goto no_mapping;
default:
goto error;
}
no_mapping:
line = getnextline(f);
switch(line) {
case EOF_LINE: goto done;
case ALIAS_LINE:
if(numaliases < MAXALIASES) {
aliases[numaliases] = (char*)xalloc(strlen(keyword_value)+1);
if(aliases[numaliases] == NULL)
goto error;
strcpy(aliases[numaliases], keyword_value);
numaliases++;
}
goto no_mapping;
case SIZE_LINE:
encoding->size = value1;
encoding->row_size = value2;
goto no_mapping;
case FIRSTINDEX_LINE:
encoding->first = value1;
encoding->first_col = value2;
goto no_mapping;
case STARTMAPPING_LINE:
if(headerOnly)
goto done;
if(!strcasecmp(keyword_value, "unicode")) {
mapping = (FontMapPtr)xalloc(sizeof(FontMapRec));
if(mapping == NULL)
goto error;
mapping->type = FONT_ENCODING_UNICODE;
mapping->pid = 0;
mapping->eid = 0;
mapping->recode = 0;
mapping->name = 0;
mapping->client_data = 0;
mapping->next = 0;
goto mapping;
} else if(!strcasecmp(keyword_value, "cmap")) {
mapping = (FontMapPtr)xalloc(sizeof(FontMapRec));
if(mapping == NULL)
goto error;
mapping->type = FONT_ENCODING_TRUETYPE;
mapping->pid = value1;
mapping->eid = value2;
mapping->recode = 0;
mapping->name = 0;
mapping->client_data = 0;
mapping->next = 0;
goto mapping;
} else if(!strcasecmp(keyword_value, "postscript")) {
mapping = (FontMapPtr)xalloc(sizeof(FontMapRec));
if(mapping == NULL)
goto error;
mapping->type = FONT_ENCODING_POSTSCRIPT;
mapping->pid = 0;
mapping->eid = 0;
mapping->recode = 0;
mapping->name = 0;
mapping->client_data = 0;
mapping->next = 0;
goto string_mapping;
} else {
goto skipmapping;
}
goto error;
default: goto no_mapping;
}
skipmapping:
line = getnextline(f);
switch(line) {
case ENDMAPPING_LINE:
goto no_mapping;
case EOF_LINE:
goto error;
default:
goto skipmapping;
}
mapping:
line = getnextline(f);
switch(line) {
case EOF_LINE: goto error;
case ENDMAPPING_LINE:
mapping->recode = FontEncSimpleRecode;
mapping->name = FontEncUndefinedName;
mapping->client_data = sm =
(FontEncSimpleMapPtr)xalloc(sizeof(FontEncSimpleMapRec));
if(sm == NULL)
goto error;
sm->row_size = encoding->row_size;
if(first <= last) {
sm->first = first;
sm->len=last-first+1;
sm->map =
(unsigned short*)xalloc(sm->len * sizeof(unsigned short));
if(sm->map == NULL) {
xfree(sm);
mapping->client_data = sm = NULL;
goto error;
}
} else {
sm->first = 0;
sm->len = 0;
sm->map = 0;
}
for(i=0; i < sm->len; i++)
sm -> map[i] = enc[first+i];
install_mapping(encoding, mapping);
mapping = 0;
first = 0xFFFF; last=0;
goto no_mapping;
case CODE_LINE:
if(setCode(value1, value2, encoding->row_size,
&first, &last, &encsize, &enc))
goto error;
goto mapping;
case CODE_RANGE_LINE:
if(value1 > 0x10000)
value1 = 0x10000;
if(value2 > 0x10000)
value2 = 0x10000;
if(value2 < value1)
goto mapping;
if(setCode(value2, value3+(value2-value1), encoding->row_size,
&first, &last, &encsize, &enc))
goto error;
for(i=value1; i<value2; i++) {
if(setCode(i, value3+(i-value1), encoding->row_size,
&first, &last, &encsize, &enc))
goto error;
}
goto mapping;
case CODE_UNDEFINE_LINE:
if(value1 > 0x10000)
value1 = 0x10000;
if(value2 > 0x10000)
value2 = 0x10000;
if(value2 < value1)
goto mapping;
if(setCode(value2, 0, encoding->row_size,
&first, &last, &encsize, &enc))
goto error;
for(i = value1; i < value2; i++) {
if(setCode(i, 0, encoding->row_size,
&first, &last, &encsize, &enc))
goto error;
}
goto mapping;
default: goto mapping;
}
string_mapping:
line = getnextline(f);
switch(line) {
case EOF_LINE: goto error;
case ENDMAPPING_LINE:
mapping->recode = FontEncUndefinedRecode;
mapping->name = FontEncSimpleName;
mapping->client_data = sn =
(FontEncSimpleNamePtr)xalloc(sizeof(FontEncSimpleNameRec));
if(sn == NULL)
goto error;
if(first > last) {
xfree(sn);
mapping->client_data = sn = NULL;
goto error;
}
sn->first = first;
sn->len = last - first + 1;
sn->map = (char**)xalloc(sn->len*sizeof(char*));
if(sn->map == NULL) {
xfree(sn);
mapping->client_data = sn = NULL;
goto error;
}
for(i = 0; i < sn->len; i++)
sn->map[i] = nam[first+i];
install_mapping(encoding,mapping);
mapping = 0;
first = 0xFFFF; last=0;
goto no_mapping;
case NAME_LINE:
if(value1 >= 0x10000) goto string_mapping;
if(namsize == 0) {
namsize = (value1) < 256 ? 256 : 0x10000;
nam = (char**)xalloc(namsize * sizeof(char*));
if(nam == NULL) {
namsize=0;
goto error;
}
} else if(namsize <= value1) {
namsize = 0x10000;
if((newnam = (char**)xrealloc(nam, namsize)) == NULL)
goto error;
nam = newnam;
}
if(first > last) {
first = last = value1;
}
if(value1 < first) {
for(i = value1; i < first; i++)
nam[i] = NULL;
first = value1;
}
if(value1 > last) {
for(i=last+1; i <= value1; i++)
nam[i]=NULL;
last = value1;
}
nam[value1] = (char*)xalloc(strlen(keyword_value)+1);
if(nam[value1] == NULL) {
goto error;
}
strcpy(nam[value1], keyword_value);
goto string_mapping;
default: goto string_mapping;
}
done:
if(encsize) xfree(enc); encsize=0; enc = NULL;
if(namsize) xfree(nam); namsize=0; nam = NULL;
encoding->aliases=NULL;
if(numaliases) {
encoding->aliases = (char**)xalloc((numaliases+1)*sizeof(char*));
if(encoding->aliases == NULL)
goto error;
for(i=0; i<numaliases; i++)
encoding->aliases[i] = aliases[i];
encoding->aliases[numaliases]=NULL;
}
return encoding;
error:
if(encsize) xfree(enc); encsize=0;
if(namsize) {
for(i = first; i <= last; i++)
if(nam[i])
xfree(nam[i]);
xfree(nam);
namsize = 0;
}
if(mapping) {
if(mapping->client_data) xfree(mapping->client_data);
xfree(mapping);
}
if(encoding) {
if(encoding->name) xfree(encoding->name);
for(mapping = encoding->mappings; mapping; mapping = mapping->next) {
if(mapping->client_data) xfree(mapping->client_data);
xfree(mapping);
}
xfree(encoding);
}
for(i = 0; i < numaliases; i++)
xfree(aliases[i]);
return 0;
}
char*
FontEncDirectory()
{
static char* dir = NULL;
if(dir == NULL) {
char *c = getenv("FONT_ENCODINGS_DIRECTORY");
if(c) {
dir = malloc(strlen(c) + 1);
if(!dir)
return NULL;
strcpy(dir, c);
} else {
dir = FONT_ENCODINGS_DIRECTORY;
}
}
return dir;
}
static void
parseFontFileName(const char *fontFileName, char *buf, char *dir)
{
const char *p;
char *q, *lastslash;
for(p = fontFileName, q = dir, lastslash = NULL; *p; p++, q++) {
*q = *p;
if(*p == '/')
lastslash = q+1;
}
if(!lastslash)
lastslash = dir;
*lastslash = '\0';
if(buf && strlen(dir) + 14 < MAXFONTFILENAMELEN) {
strcpy(buf, dir);
strcat(buf, "encodings.dir");
}
}
static FontEncPtr
FontEncReallyReallyLoad(const char *charset,
const char *dirname, const char *dir)
{
FontFilePtr f;
FILE *file;
FontEncPtr encoding;
char file_name[MAXFONTFILENAMELEN], encoding_name[MAXFONTNAMELEN],
buf[MAXFONTFILENAMELEN];
int count, n;
static char format[24] = "";
if((file = fopen(dirname, "r")) == NULL) {
return NULL;
}
count = fscanf(file, "%d\n", &n);
if(count == EOF || count != 1) {
fclose(file);
return NULL;
}
encoding = NULL;
if (!format[0]) {
sprintf(format, "%%%ds %%%d[^\n]\n", (int)sizeof(encoding_name) - 1,
(int)sizeof(file_name) - 1);
}
for(;;) {
count = fscanf(file, format, encoding_name, file_name);
if(count == EOF)
break;
if(count != 2)
break;
if(!strcasecmp(encoding_name, charset)) {
if(file_name[0] != '/') {
if(strlen(dir) + strlen(file_name) >= MAXFONTFILENAMELEN)
return NULL;
strcpy(buf, dir);
strcat(buf, file_name);
} else {
strcpy(buf , file_name);
}
f = FontFileOpen(buf);
if(f == NULL) {
return NULL;
}
encoding = parseEncodingFile(f, 0);
FontFileClose(f);
break;
}
}
fclose(file);
return encoding;
}
FontEncPtr
FontEncReallyLoad(const char *charset, const char *fontFileName)
{
FontEncPtr encoding;
char dir[MAXFONTFILENAMELEN], dirname[MAXFONTFILENAMELEN];
char *d;
if(fontFileName) {
parseFontFileName(fontFileName, dirname, dir);
encoding = FontEncReallyReallyLoad(charset, dirname, dir);
if(encoding)
return(encoding);
}
d = FontEncDirectory();
if(d) {
parseFontFileName(d, NULL, dir);
encoding = FontEncReallyReallyLoad(charset, d, dir);
return encoding;
}
return NULL;
}
char **
FontEncIdentify(const char *fileName)
{
FontFilePtr f;
FontEncPtr encoding;
char **names, **name, **alias;
int numaliases;
if((f = FontFileOpen(fileName))==NULL) {
return NULL;
}
encoding = parseEncodingFile(f, 1);
FontFileClose(f);
if(!encoding)
return NULL;
numaliases = 0;
if(encoding->aliases)
for(alias = encoding->aliases; *alias; alias++)
numaliases++;
names = (char**)xalloc((numaliases+2)*sizeof(char*));
if(names == NULL) {
if(encoding->aliases)
xfree(encoding->aliases);
xfree(encoding);
return NULL;
}
name = names;
*(name++) = encoding->name;
if(numaliases > 0)
for(alias = encoding->aliases; *alias; alias++, name++)
*name = *alias;
*name = 0;
xfree(encoding->aliases);
xfree(encoding);
return names;
}