encparse.c revision 55acc8fc
11.80Sjmcneill/*
21.1StvCopyright (c) 1998-2001 by Juliusz Chroboczek
31.1Stv
41.1StvPermission is hereby granted, free of charge, to any person obtaining a copy
51.62Sapbof this software and associated documentation files (the "Software"), to deal
61.62Sapbin the Software without restriction, including without limitation the rights
71.62Sapbto use, copy, modify, merge, publish, distribute, sublicense, and/or sell
81.62Sapbcopies of the Software, and to permit persons to whom the Software is
91.62Sapbfurnished to do so, subject to the following conditions:
101.62Sapb
111.62SapbThe above copyright notice and this permission notice shall be included in
121.62Sapball copies or substantial portions of the Software.
131.62Sapb
141.62SapbTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
151.62SapbIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161.62SapbFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
171.64SapbAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
181.64SapbLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
191.64SapbOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
201.62SapbTHE SOFTWARE.
211.1Stv*/
221.40Ssalo
231.56Swiz/* Parser for encoding files */
241.1Stv
251.20Sbjh21/* This code assumes that we are using ASCII.  We don't use the ctype
261.56Swiz   functions, as they depend on the current locale.  On the other
271.72Sapb   hand, we do use strcasecmp, but only on strings that we've checked
281.56Swiz   to be pure ASCII.  Bloody ``Code Set Independence''. */
291.56Swiz
301.56Swiz#include <string.h>
311.56Swiz#include <stdio.h>
321.56Swiz
331.56Swiz#if defined(__SCO__) || defined(__UNIXWARE__)
341.56Swiz#include <strings.h>
351.44Sjmc#endif
361.44Sjmc
371.44Sjmc#include <stdlib.h>
381.44Sjmc
391.44Sjmc#include "zlib.h"
401.44Sjmctypedef gzFile FontFilePtr;
411.44Sjmc#define FontFileGetc(f) gzgetc(f)
421.44Sjmc#define FontFileOpen(filename) gzopen(filename, "rb")
431.44Sjmc#define FontFileClose(f) gzclose(f)
441.44Sjmc
451.53Swiz#define MAXFONTFILENAMELEN 1024
461.53Swiz#define MAXFONTNAMELEN 1024
471.53Swiz
481.44Sjmc#include <X11/fonts/fontenc.h>
491.44Sjmc#include "fontencI.h"
501.44Sjmc
511.44Sjmc#define MAXALIASES 20
521.20Sbjh21
531.20Sbjh21#define EOF_TOKEN -1
541.20Sbjh21#define ERROR_TOKEN -2
551.20Sbjh21#define EOL_TOKEN 0
561.53Swiz#define NUMBER_TOKEN 1
571.1Stv#define KEYWORD_TOKEN 2
581.4Stv
591.6Stv#define EOF_LINE -1
601.6Stv#define ERROR_LINE -2
611.6Stv#define STARTENCODING_LINE 1
621.6Stv#define STARTMAPPING_LINE 2
631.6Stv#define ENDMAPPING_LINE 3
641.6Stv#define CODE_LINE 4
651.6Stv#define CODE_RANGE_LINE 5
661.6Stv#define CODE_UNDEFINE_LINE 6
671.6Stv#define NAME_LINE 7
681.4Stv#define SIZE_LINE 8
691.4Stv#define ALIAS_LINE 9
701.4Stv#define FIRSTINDEX_LINE 10
711.4Stv
721.69Sdbj/* Return from lexer */
731.34Sthorpej#define MAXKEYWORDLEN 100
741.34Sthorpej
751.28Sbriggsstatic long number_value;
761.22Sthorpejstatic char keyword_value[MAXKEYWORDLEN+1];
771.4Stv
781.4Stvstatic long value1, value2, value3;
791.4Stv
801.6Stv/* Lexer code */
811.69Sdbj
821.75Schristos/* Skip to the beginning of new line */
831.72Sapbstatic void
841.72SapbskipEndOfLine(FontFilePtr f, int c)
851.72Sapb{
861.11Stv    if(c == 0)
871.44Sjmc        c = FontFileGetc(f);
881.41Slukem
891.72Sapb    for(;;)
901.72Sapb        if(c <= 0 || c == '\n')
911.11Stv            return;
921.11Stv        else
931.11Stv            c = FontFileGetc(f);
941.11Stv}
951.11Stv
961.4Stv/* Get a number; we're at the first digit. */
971.4Stvstatic unsigned
981.1Stvgetnum(FontFilePtr f, int c, int *cp)
991.44Sjmc{
1001.53Swiz    unsigned n = 0;
1011.53Swiz    int base = 10;
1021.35Sthorpej
1031.77Spooka    /* look for `0' or `0x' prefix */
1041.75Schristos    if(c == '0') {
1051.77Spooka        c = FontFileGetc(f);
1061.35Sthorpej        base = 8;
1071.6Stv        if(c == 'x' || c == 'X') {
1081.9Stv            base = 16;
1091.9Stv            c = FontFileGetc(f);
1101.9Stv        }
1111.6Stv    }
1121.9Stv
1131.9Stv    /* accumulate digits */
1141.53Swiz    for(;;) {
1151.53Swiz        if ('0' <= c && c <= '9') {
1161.9Stv            n *= base; n += c - '0';
1171.9Stv        } else if('a' <= c && c <= 'f') {
1181.6Stv            n *= base; n += c - 'a' + 10;
1191.6Stv        } else if('A' <=c && c <= 'F') {
1201.53Swiz            n *= base; n += c - 'A' + 10;
1211.53Swiz        } else
1221.6Stv            break;
1231.6Stv        c = FontFileGetc(f);
1241.6Stv    }
1251.6Stv
1261.6Stv    *cp = c; return n;
1271.6Stv}
1281.6Stv
1291.6Stv/* Skip to beginning of new line; return 1 if only whitespace was found. */
1301.4Stvstatic int
1311.4StvendOfLine(FontFilePtr f, int c)
1321.58Sthorpej{
1331.32Sthorpej    if(c == 0)
1341.32Sthorpej        c = FontFileGetc(f);
1351.7Stv
1361.49Sjmc    for(;;) {
1371.49Sjmc        if(c <= 0 || c == '\n')
1381.49Sjmc            return 1;
1391.46Sdbj        else if(c == '#') {
1401.80Sjmcneill            skipEndOfLine(f,c);
1411.4Stv            return 1;
1421.4Stv        }
1431.7Stv        else if(c == ' ' || c == '\t') {
1441.7Stv            skipEndOfLine(f,c);
1451.7Stv            return 0;
1461.7Stv        }
1471.77Spooka        c = FontFileGetc(f);
1481.77Spooka    }
1491.77Spooka}
1501.7Stv
1511.4Stv/* Get a token; we're at first char */
1521.4Stvstatic int
1531.4Stvgettoken(FontFilePtr f, int c, int *cp)
1541.19Sbjh21{
1551.39Satatat    char *p;
1561.76Schristos
1571.74Sjoerg    if(c <= 0)
1581.48Sjmc    c = FontFileGetc(f);
1591.72Sapb
1601.72Sapb    if(c <= 0) {
1611.78Sapb        return EOF_TOKEN;
1621.78Sapb    }
1631.61Snakayama
1641.66Schristos    while(c == ' ' || c == '\t')
1651.8Stv        c = FontFileGetc(f);
1661.66Schristos
1671.66Schristos    if(c=='\n') {
1681.66Schristos        return EOL_TOKEN;
1691.66Schristos    } else if(c == '#') {
1701.66Schristos        skipEndOfLine(f,c);
1711.66Schristos        return EOL_TOKEN;
1721.66Schristos    } else if(c >= '0' && c <= '9') {
1731.54Swiz        number_value = getnum(f,c,cp);
1741.66Schristos        return NUMBER_TOKEN;
1751.66Schristos    } else if((c >= 'A' && c <= 'Z') ||
1761.66Schristos              (c >= 'a' && c <= 'z') ||
1771.66Schristos              c == '/' || c == '_' || c == '-' || c == '.') {
1781.54Swiz        p = keyword_value;
1791.72Sapb        *p++ = c;
1801.72Sapb        while(p-keyword_value < MAXKEYWORDLEN) {
1811.72Sapb            c = FontFileGetc(f);
1821.44Sjmc            if(c <= ' ' || c > '~' || c == '#')
1831.54Swiz                break;
1841.44Sjmc            *p++ = c;
1851.72Sapb        }
1861.72Sapb        *cp = c;
1871.72Sapb        *p = '\0';
1881.63Sapb        return KEYWORD_TOKEN;
1891.54Swiz    } else {
1901.47Sjmc        *cp = c;
1911.8Stv        return ERROR_TOKEN;
1921.33Sthorpej    }
1931.8Stv}
1941.8Stv
1951.8Stv/* Parse a line.
1961.1Stv * Always skips to the beginning of a new line, even if an error occurs */
1971.4Stvstatic int
1981.4Stvgetnextline(FontFilePtr f)
1991.1Stv{
2001.4Stv    int c, token;
2011.4Stv    c = FontFileGetc(f);
2021.4Stv    if(c <= 0)
2031.4Stv        return EOF_LINE;
2041.1Stv
2051.79Sjmcneill  again:
2061.79Sjmcneill    token=gettoken(f,c,&c);
2071.68Stnozaki
2081.68Stnozaki    switch(token) {
2091.68Stnozaki    case EOF_TOKEN:
2101.68Stnozaki        return EOF_LINE;
2111.68Stnozaki    case EOL_TOKEN:
2121.68Stnozaki        /* empty line */
2131.68Stnozaki        c = FontFileGetc(f);
2141.68Stnozaki        goto again;
2151.68Stnozaki    case NUMBER_TOKEN:
2161.68Stnozaki        value1 = number_value;
2171.68Stnozaki        token = gettoken(f,c,&c);
2181.68Stnozaki        switch(token) {
2191.68Stnozaki        case NUMBER_TOKEN:
2201.68Stnozaki            value2 = number_value;
2211.68Stnozaki            token = gettoken(f,c,&c);
2221.68Stnozaki            switch(token) {
2231.68Stnozaki            case NUMBER_TOKEN:
2241.68Stnozaki                value3 = number_value;
2251.68Stnozaki                return CODE_RANGE_LINE;
2261.68Stnozaki            case EOL_TOKEN:
2271.68Stnozaki                return CODE_LINE;
2281.68Stnozaki            default:
2291.68Stnozaki                skipEndOfLine(f,c);
2301.68Stnozaki                return ERROR_LINE;
2311.68Stnozaki            }
2321.68Stnozaki        case KEYWORD_TOKEN:
2331.68Stnozaki            if(!endOfLine(f,c))
2341.68Stnozaki                return ERROR_LINE;
2351.68Stnozaki            else
2361.68Stnozaki                return NAME_LINE;
2371.68Stnozaki        default:
2381.68Stnozaki            skipEndOfLine(f,c);
2391.68Stnozaki            return ERROR_LINE;
2401.68Stnozaki        }
2411.68Stnozaki    case KEYWORD_TOKEN:
2421.68Stnozaki        if(!strcasecmp(keyword_value, "STARTENCODING")) {
2431.73Sapb            token = gettoken(f,c,&c);
2441.73Sapb            if(token == KEYWORD_TOKEN) {
2451.73Sapb                if(endOfLine(f,c))
2461.1Stv                    return STARTENCODING_LINE;
247                else
248                    return ERROR_LINE;
249            } else {
250                skipEndOfLine(f,c);
251                return ERROR_LINE;
252            }
253        } else if(!strcasecmp(keyword_value, "ALIAS")) {
254            token = gettoken(f,c,&c);
255            if(token == KEYWORD_TOKEN) {
256                if(endOfLine(f,c))
257                    return ALIAS_LINE;
258                else
259                    return ERROR_LINE;
260            } else {
261                skipEndOfLine(f,c);
262                return ERROR_LINE;
263            }
264        } else if(!strcasecmp(keyword_value, "SIZE")) {
265            token = gettoken(f,c,&c);
266            if(token == NUMBER_TOKEN) {
267                value1 = number_value;
268                token = gettoken(f,c,&c);
269                switch(token) {
270                case NUMBER_TOKEN:
271                    value2 = number_value;
272                    return SIZE_LINE;
273                case EOL_TOKEN:
274                    value2=0;
275                    return SIZE_LINE;
276                default:
277                    skipEndOfLine(f,c);
278                    return ERROR_LINE;
279                }
280            } else {
281                skipEndOfLine(f,c);
282                return ERROR_LINE;
283            }
284        } else if(!strcasecmp(keyword_value, "FIRSTINDEX")) {
285            token = gettoken(f,c,&c);
286            if(token == NUMBER_TOKEN) {
287                value1 = number_value;
288                token = gettoken(f,c,&c);
289                switch(token) {
290                case NUMBER_TOKEN:
291                    value2 = number_value;
292                    return FIRSTINDEX_LINE;
293                case EOL_TOKEN:
294                    value2 = 0;
295                    return FIRSTINDEX_LINE;
296                default:
297                    skipEndOfLine(f,c);
298                    return ERROR_LINE;
299                }
300            } else {
301                skipEndOfLine(f,c);
302                return ERROR_LINE;
303            }
304        } else if(!strcasecmp(keyword_value, "STARTMAPPING")) {
305            keyword_value[0] = 0;
306            value1 = 0; value1 = 0;
307            /* first a keyword */
308            token = gettoken(f,c,&c);
309            if(token != KEYWORD_TOKEN) {
310                skipEndOfLine(f, c);
311                return ERROR_LINE;
312            }
313
314            /* optional first integer */
315            token = gettoken(f,c,&c);
316            if(token == NUMBER_TOKEN) {
317                value1 = number_value;
318            } else if(token == EOL_TOKEN) {
319                return STARTMAPPING_LINE;
320            } else {
321                skipEndOfLine(f, c);
322                return ERROR_LINE;
323            }
324
325            /* optional second integer */
326            token = gettoken(f,c,&c);
327            if(token == NUMBER_TOKEN) {
328                value2 = number_value;
329            } else if(token == EOL_TOKEN) {
330                return STARTMAPPING_LINE;
331            } else {
332                skipEndOfLine(f, c);
333                return ERROR_LINE;
334            }
335
336            if(!endOfLine(f,c))
337                return ERROR_LINE;
338            else {
339                return STARTMAPPING_LINE;
340            }
341        } else if(!strcasecmp(keyword_value, "UNDEFINE")) {
342            /* first integer */
343            token = gettoken(f,c,&c);
344            if(token != NUMBER_TOKEN) {
345                skipEndOfLine(f,c);
346                return ERROR_LINE;
347            }
348            value1 = number_value;
349            /* optional second integer */
350            token = gettoken(f,c,&c);
351            if(token == EOL_TOKEN) {
352                value2 = value1;
353                return CODE_UNDEFINE_LINE;
354            } else if(token == NUMBER_TOKEN) {
355                value2 = number_value;
356                if(endOfLine(f,c)) {
357                    return CODE_UNDEFINE_LINE;
358                } else
359                    return ERROR_LINE;
360            } else {
361                skipEndOfLine(f,c);
362                return ERROR_LINE;
363            }
364        } else if(!strcasecmp(keyword_value, "ENDENCODING")) {
365            if(endOfLine(f,c))
366                return EOF_LINE;
367            else
368                return ERROR_LINE;
369        } else if(!strcasecmp(keyword_value, "ENDMAPPING")) {
370            if(endOfLine(f,c))
371                return ENDMAPPING_LINE;
372            else
373                return ERROR_LINE;
374        } else {
375            skipEndOfLine(f,c);
376            return ERROR_LINE;
377        }
378    default:
379        return ERROR_LINE;
380    }
381}
382
383static void
384install_mapping(FontEncPtr encoding, FontMapPtr mapping)
385{
386    FontMapPtr m;
387
388    if(encoding->mappings == NULL)
389        encoding->mappings = mapping;
390    else {
391        m = encoding->mappings;
392        while(m->next != NULL)
393            m = m->next;
394        m->next = mapping;
395    }
396    mapping->next = NULL;
397    mapping->encoding = encoding;
398}
399
400static int
401setCode(unsigned from, unsigned to, unsigned row_size,
402        unsigned *first, unsigned *last,
403        unsigned *encsize, unsigned short **enc)
404{
405    unsigned index, i;
406    unsigned short *newenc;
407
408    if(from>0xFFFF)
409        return 0;               /* success */
410
411    if(row_size==0)
412        index=from;
413    else {
414        if((value1 & 0xFF) >= row_size)
415            return 0;           /* ignore out of range mappings */
416        index = (from>>8) * row_size + (from&0xFF);
417    }
418
419    /* Optimize away useless identity mappings.  This is only expected
420       to be useful with linear encodings. */
421    if(index == to && (index < *first || index > *last))
422        return 0;
423    if(*encsize == 0) {
424        *encsize = (index < 256) ? 256 : 0x10000;
425        *enc = malloc((*encsize) * sizeof(unsigned short));
426        if(*enc == NULL) {
427            *encsize = 0;
428            return 1;
429        }
430    } else if(*encsize <= index) {
431        *encsize = 0x10000;
432        if((newenc = realloc(enc, *encsize))==NULL)
433            return 1;
434        *enc = newenc;
435    }
436    if(*first > *last) {
437        *first = *last = index;
438    }
439    if(index < *first) {
440        for(i = index; i < *first; i++)
441            (*enc)[i] = i;
442        *first = index;
443    }
444    if(index > *last) {
445        for(i = *last + 1; i <= index; i++)
446            (*enc)[i] = i;
447        *last = index;
448    }
449    (*enc)[index] = to;
450    return 0;
451}
452
453/* Parser.  If headerOnly is true, we're only interested in the
454   data contained in the encoding file's header. */
455
456/* As font encodings are currently never freed, the allocations done
457   by this function are mostly its private business.  Note, however,
458   that FontEncIdentify needs to free the header fields -- so if you
459   change this function, you may need to change FontEncIdentify. */
460
461/* I want a garbage collector. */
462
463static FontEncPtr
464parseEncodingFile(FontFilePtr f, int headerOnly)
465{
466    int line;
467
468    unsigned short *enc=NULL;
469    char **nam = NULL, **newnam;
470    unsigned i, first = 0xFFFF, last=0, encsize=0, namsize=0;
471    FontEncPtr encoding = NULL;
472    FontMapPtr mapping = NULL;
473    FontEncSimpleMapPtr sm;
474    FontEncSimpleNamePtr sn;
475    char *aliases[MAXALIASES];
476    int numaliases=0;
477
478#if 0
479    /* GCC complains about unused labels.  Please fix GCC rather than
480       obfuscating my code. */
481  no_encoding:
482#endif
483    line = getnextline(f);
484    switch(line) {
485    case EOF_LINE:
486        goto error;
487    case STARTENCODING_LINE:
488        encoding = malloc(sizeof(FontEncRec));
489        if(encoding == NULL)
490            goto error;
491        encoding->name = strdup(keyword_value);
492        if(encoding->name == NULL)
493            goto error;
494        encoding->size = 256;
495        encoding->row_size = 0;
496        encoding->mappings = NULL;
497        encoding->next = NULL;
498        encoding->first = encoding->first_col=0;
499        goto no_mapping;
500    default:
501        goto error;
502    }
503
504  no_mapping:
505    line = getnextline(f);
506    switch(line) {
507    case EOF_LINE: goto done;
508    case ALIAS_LINE:
509        if(numaliases < MAXALIASES) {
510            aliases[numaliases] = strdup(keyword_value);
511            if(aliases[numaliases] == NULL)
512                goto error;
513            numaliases++;
514        }
515        goto no_mapping;
516    case SIZE_LINE:
517        encoding->size = value1;
518        encoding->row_size = value2;
519        goto no_mapping;
520    case FIRSTINDEX_LINE:
521        encoding->first = value1;
522        encoding->first_col = value2;
523        goto no_mapping;
524    case STARTMAPPING_LINE:
525        if(headerOnly)
526            goto done;
527        if(!strcasecmp(keyword_value, "unicode")) {
528            mapping = malloc(sizeof(FontMapRec));
529            if(mapping == NULL)
530                goto error;
531            mapping->type = FONT_ENCODING_UNICODE;
532            mapping->pid = 0;
533            mapping->eid = 0;
534            mapping->recode = NULL;
535            mapping->name = NULL;
536            mapping->client_data = NULL;
537            mapping->next = NULL;
538            goto mapping;
539        } else if(!strcasecmp(keyword_value, "cmap")) {
540            mapping = malloc(sizeof(FontMapRec));
541            if(mapping == NULL)
542                goto error;
543            mapping->type = FONT_ENCODING_TRUETYPE;
544            mapping->pid = value1;
545            mapping->eid = value2;
546            mapping->recode = NULL;
547            mapping->name = NULL;
548            mapping->client_data = NULL;
549            mapping->next = NULL;
550            goto mapping;
551        } else if(!strcasecmp(keyword_value, "postscript")) {
552            mapping = malloc(sizeof(FontMapRec));
553            if(mapping == NULL)
554                goto error;
555            mapping->type = FONT_ENCODING_POSTSCRIPT;
556            mapping->pid = 0;
557            mapping->eid = 0;
558            mapping->recode = NULL;
559            mapping->name = NULL;
560            mapping->client_data = NULL;
561            mapping->next = NULL;
562            goto string_mapping;
563        } else {                /* unknown mapping type -- ignore */
564            goto skipmapping;
565        }
566        /* NOTREACHED */
567        goto error;
568    default: goto no_mapping;   /* ignore unknown lines */
569    }
570
571  skipmapping:
572    line = getnextline(f);
573    switch(line) {
574    case ENDMAPPING_LINE:
575        goto no_mapping;
576    case EOF_LINE:
577        goto error;
578    default:
579        goto skipmapping;
580    }
581
582  mapping:
583    line = getnextline(f);
584    switch(line) {
585    case EOF_LINE: goto error;
586    case ENDMAPPING_LINE:
587        mapping->recode = FontEncSimpleRecode;
588        mapping->name = FontEncUndefinedName;
589        mapping->client_data = sm = malloc(sizeof(FontEncSimpleMapRec));
590        if(sm == NULL)
591            goto error;
592        sm->row_size = encoding->row_size;
593        if(first <= last) {
594            unsigned short *newmap;
595
596            sm->first = first;
597            sm->len=last-first+1;
598            newmap = malloc(sm->len * sizeof(unsigned short));
599            if(newmap == NULL) {
600                free(sm);
601                mapping->client_data = sm = NULL;
602                goto error;
603            }
604	    for(i=0; i < sm->len; i++)
605		newmap[i] = enc[first+i];
606	    sm->map = newmap;
607        } else {
608            sm->first = 0;
609            sm->len = 0;
610            sm->map = NULL;
611        }
612        install_mapping(encoding, mapping);
613        mapping = NULL;
614        first = 0xFFFF; last=0;
615        goto no_mapping;
616
617    case CODE_LINE:
618        if(setCode(value1, value2, encoding->row_size,
619                   &first, &last, &encsize, &enc))
620            goto error;
621        goto mapping;
622
623    case CODE_RANGE_LINE:
624        if(value1 > 0x10000)
625            value1 = 0x10000;
626        if(value2 > 0x10000)
627            value2 = 0x10000;
628        if(value2 < value1)
629            goto mapping;
630        /* Do the last value first to avoid having to realloc() */
631        if(setCode(value2, value3+(value2-value1), encoding->row_size,
632                   &first, &last, &encsize, &enc))
633            goto error;
634        for(i=value1; i<value2; i++) {
635            if(setCode(i, value3+(i-value1), encoding->row_size,
636                       &first, &last, &encsize, &enc))
637                goto error;
638        }
639        goto mapping;
640
641    case CODE_UNDEFINE_LINE:
642        if(value1 > 0x10000)
643            value1 = 0x10000;
644        if(value2 > 0x10000)
645            value2 = 0x10000;
646        if(value2 < value1)
647            goto mapping;
648        /* Do the last value first to avoid having to realloc() */
649        if(setCode(value2, 0, encoding->row_size,
650                   &first, &last, &encsize, &enc))
651            goto error;
652        for(i = value1; i < value2; i++) {
653            if(setCode(i, 0, encoding->row_size,
654                       &first, &last, &encsize, &enc))
655                goto error;
656        }
657        goto mapping;
658
659    default: goto mapping;      /* ignore unknown lines */
660    }
661
662  string_mapping:
663    line = getnextline(f);
664    switch(line) {
665    case EOF_LINE: goto error;
666    case ENDMAPPING_LINE:
667        mapping->recode = FontEncUndefinedRecode;
668        mapping->name = FontEncSimpleName;
669        mapping->client_data = sn = malloc(sizeof(FontEncSimpleNameRec));
670        if(sn == NULL)
671            goto error;
672        if(first > last) {
673            free(sn);
674            mapping->client_data = sn = NULL;
675            goto error;
676        }
677        sn->first = first;
678        sn->len = last - first + 1;
679        sn->map = malloc(sn->len*sizeof(char*));
680        if(sn->map == NULL) {
681            free(sn);
682            mapping->client_data = sn = NULL;
683            goto error;
684        }
685        for(i = 0; i < sn->len; i++)
686            sn->map[i] = nam[first+i];
687        install_mapping(encoding,mapping);
688        mapping = NULL;
689        first = 0xFFFF; last=0;
690        goto no_mapping;
691    case NAME_LINE:
692        if(value1 >= 0x10000) goto string_mapping;
693        if(namsize == 0) {
694            namsize = (value1) < 256 ? 256 : 0x10000;
695            nam = malloc(namsize * sizeof(char*));
696            if(nam == NULL) {
697                namsize=0;
698                goto error;
699            }
700        } else if(namsize <= value1) {
701            namsize = 0x10000;
702            if((newnam = (char**)realloc(nam, namsize)) == NULL)
703                goto error;
704            nam = newnam;
705        }
706        if(first > last) {
707            first = last = value1;
708        }
709        if(value1 < first) {
710            for(i = value1; i < first; i++)
711                nam[i] = NULL;
712            first = value1;
713        }
714        if(value1 > last) {
715            for(i=last+1; i <= value1; i++)
716                nam[i]=NULL;
717            last = value1;
718        }
719        nam[value1] = strdup(keyword_value);
720        if(nam[value1] == NULL) {
721            goto error;
722        }
723        goto string_mapping;
724
725    default: goto string_mapping; /* ignore unknown lines */
726    }
727
728  done:
729    if(encsize) free(enc); encsize=0; enc = NULL;
730    if(namsize) free(nam); namsize=0; nam = NULL; /* don't free entries! */
731
732    encoding->aliases=NULL;
733    if(numaliases) {
734        encoding->aliases = malloc((numaliases+1)*sizeof(char*));
735        if(encoding->aliases == NULL)
736            goto error;
737        for(i=0; i<numaliases; i++)
738            encoding->aliases[i] = aliases[i];
739        encoding->aliases[numaliases]=NULL;
740    }
741
742    return encoding;
743
744error:
745    if(encsize) free(enc); encsize=0;
746    if(namsize) {
747        for(i = first; i <= last; i++)
748            free(nam[i]);
749        free(nam);
750        namsize = 0;
751    }
752    if(mapping) {
753        free(mapping->client_data);
754        free(mapping);
755    }
756    if(encoding) {
757	FontMapPtr nextmap;
758	free(encoding->name);
759	for (mapping = encoding->mappings; mapping; mapping = nextmap) {
760	    free(mapping->client_data);
761	    nextmap = mapping->next;
762	    free(mapping);
763	}
764	free(encoding);
765    }
766    for(i = 0; i < numaliases; i++)
767        free(aliases[i]);
768    /* We don't need to free sn and sm as they handled locally in the body.*/
769    return NULL;
770}
771
772char*
773FontEncDirectory(void)
774{
775    static char* dir = NULL;
776
777    if(dir == NULL) {
778        char *c = getenv("FONT_ENCODINGS_DIRECTORY");
779        if(c) {
780            dir = strdup(c);
781            if(!dir)
782                return NULL;
783        } else {
784            dir = FONT_ENCODINGS_DIRECTORY;
785        }
786    }
787    return dir;
788}
789
790static void
791parseFontFileName(const char *fontFileName, char *buf, char *dir)
792{
793    const char *p;
794    char *q, *lastslash;
795
796    for(p = fontFileName, q = dir, lastslash = NULL; *p; p++, q++) {
797        *q = *p;
798        if(*p == '/')
799            lastslash = q+1;
800    }
801
802    if(!lastslash)
803        lastslash = dir;
804
805    *lastslash = '\0';
806
807    if(buf && strlen(dir) + 14 < MAXFONTFILENAMELEN) {
808        strcpy(buf, dir);
809        strcat(buf, "encodings.dir");
810    }
811}
812
813static FontEncPtr
814FontEncReallyReallyLoad(const char *charset,
815                        const char *dirname, const char *dir)
816{
817    FontFilePtr f;
818    FILE *file;
819    FontEncPtr encoding;
820    char file_name[MAXFONTFILENAMELEN], encoding_name[MAXFONTNAMELEN],
821        buf[MAXFONTFILENAMELEN];
822    int count, n;
823    static char format[24] = "";
824
825    /* As we don't really expect to open encodings that often, we don't
826       take the trouble of caching encodings directories. */
827
828    if((file = fopen(dirname, "r")) == NULL) {
829        return NULL;
830    }
831
832    count = fscanf(file, "%d\n", &n);
833    if(count == EOF || count != 1) {
834        fclose(file);
835        return NULL;
836    }
837
838    encoding = NULL;
839    if (!format[0]) {
840	sprintf(format, "%%%ds %%%d[^\n]\n", (int)sizeof(encoding_name) - 1,
841		(int)sizeof(file_name) - 1);
842    }
843    for(;;) {
844        count = fscanf(file, format, encoding_name, file_name);
845        if(count == EOF)
846            break;
847        if(count != 2)
848            break;
849
850        if(!strcasecmp(encoding_name, charset)) {
851            /* Found it */
852            if(file_name[0] != '/') {
853                if(strlen(dir) + strlen(file_name) >= MAXFONTFILENAMELEN) {
854		    fclose(file);
855                    return NULL;
856		}
857                strcpy(buf, dir);
858                strcat(buf, file_name);
859            } else {
860                strcpy(buf , file_name);
861            }
862
863            f = FontFileOpen(buf);
864            if(f == NULL) {
865		fclose(file);
866                return NULL;
867            }
868            encoding = parseEncodingFile(f, 0);
869            FontFileClose(f);
870            break;
871        }
872    }
873
874    fclose(file);
875
876    return encoding;
877}
878
879/* Parser ntrypoint -- used by FontEncLoad */
880FontEncPtr
881FontEncReallyLoad(const char *charset, const char *fontFileName)
882{
883    FontEncPtr encoding;
884    char dir[MAXFONTFILENAMELEN], dirname[MAXFONTFILENAMELEN];
885    char *d;
886
887    if(fontFileName) {
888        parseFontFileName(fontFileName, dirname, dir);
889        encoding = FontEncReallyReallyLoad(charset, dirname, dir);
890        if(encoding)
891            return(encoding);
892    }
893
894    d = FontEncDirectory();
895    if(d) {
896        parseFontFileName(d, NULL, dir);
897        encoding = FontEncReallyReallyLoad(charset, d, dir);
898        return encoding;
899    }
900
901    return NULL;
902}
903
904/* Return a NULL-terminated array of encoding names.  Note that this
905 * function has incestuous knowledge of the allocations done by
906 * parseEncodingFile. */
907
908char **
909FontEncIdentify(const char *fileName)
910{
911    FontFilePtr f;
912    FontEncPtr encoding;
913    char **names, **name, **alias;
914    int numaliases;
915
916    if((f = FontFileOpen(fileName))==NULL) {
917        return NULL;
918    }
919    encoding = parseEncodingFile(f, 1);
920    FontFileClose(f);
921
922    if(!encoding)
923        return NULL;
924
925    numaliases = 0;
926    if(encoding->aliases)
927        for(alias = encoding->aliases; *alias; alias++)
928            numaliases++;
929
930    names = malloc((numaliases+2)*sizeof(char*));
931    if(names == NULL) {
932        free(encoding->aliases);
933        free(encoding);
934        return NULL;
935    }
936
937    name = names;
938    *(name++) = encoding->name;
939    if(numaliases > 0)
940    for(alias = encoding->aliases; *alias; alias++, name++)
941        *name = *alias;
942
943    *name = NULL;
944    free(encoding->aliases);
945    free(encoding);
946
947    return names;
948}
949