encparse.c revision 3da084b3
1/* $XdotOrg: lib/fontenc/src/encparse.c,v 1.6 2006/04/10 16:15:12 alanc Exp $ */
2/*
3Copyright (c) 1998-2001 by Juliusz Chroboczek
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
22*/
23
24/* $XFree86: xc/lib/font/fontfile/encparse.c,v 1.20tsi Exp $ */
25
26/* Parser for encoding files */
27
28/* This code assumes that we are using ASCII.  We don't use the ctype
29   functions, as they depend on the current locale.  On the other
30   hand, we do use strcasecmp, but only on strings that we've checked
31   to be pure ASCII.  Bloody ``Code Set Independence''. */
32
33#include <string.h>
34#include <stdio.h>
35
36#if defined(__SCO__) || defined(__UNIXWARE__)
37#include <strings.h>
38#endif
39
40#ifndef FONTENC_NO_LIBFONT
41
42#include <X11/fonts/fntfilio.h>
43#include <X11/fonts/fntfilst.h>
44
45#else
46
47#include <stdlib.h>
48#define xalloc(n) malloc(n)
49#define xrealloc(p, n) realloc(p, n)
50#define xfree(p) free(p)
51
52#include "zlib.h"
53typedef gzFile FontFilePtr;
54#define FontFileGetc(f) gzgetc(f)
55#define FontFileOpen(filename) gzopen(filename, "rb")
56#define FontFileClose(f) gzclose(f)
57
58#define MAXFONTFILENAMELEN 1024
59#define MAXFONTNAMELEN 1024
60
61#endif
62
63#include <X11/fonts/fontenc.h>
64#include "fontencI.h"
65
66#define MAXALIASES 20
67
68#define EOF_TOKEN -1
69#define ERROR_TOKEN -2
70#define EOL_TOKEN 0
71#define NUMBER_TOKEN 1
72#define KEYWORD_TOKEN 2
73
74#define EOF_LINE -1
75#define ERROR_LINE -2
76#define STARTENCODING_LINE 1
77#define STARTMAPPING_LINE 2
78#define ENDMAPPING_LINE 3
79#define CODE_LINE 4
80#define CODE_RANGE_LINE 5
81#define CODE_UNDEFINE_LINE 6
82#define NAME_LINE 7
83#define SIZE_LINE 8
84#define ALIAS_LINE 9
85#define FIRSTINDEX_LINE 10
86
87/* Return from lexer */
88#define MAXKEYWORDLEN 100
89
90static long number_value;
91static char keyword_value[MAXKEYWORDLEN+1];
92
93static long value1, value2, value3;
94
95/* Lexer code */
96
97/* Skip to the beginning of new line */
98static void
99skipEndOfLine(FontFilePtr f, int c)
100{
101    if(c == 0)
102        c = FontFileGetc(f);
103
104    for(;;)
105        if(c <= 0 || c == '\n')
106            return;
107        else
108            c = FontFileGetc(f);
109}
110
111/* Get a number; we're at the first digit. */
112static unsigned
113getnum(FontFilePtr f, int c, int *cp)
114{
115    unsigned n = 0;
116    int base = 10;
117
118    /* look for `0' or `0x' prefix */
119    if(c == '0') {
120        c = FontFileGetc(f);
121        base = 8;
122        if(c == 'x' || c == 'X') {
123            base = 16;
124            c = FontFileGetc(f);
125        }
126    }
127
128    /* accumulate digits */
129    for(;;) {
130        if ('0' <= c && c <= '9') {
131            n *= base; n += c - '0';
132        } else if('a' <= c && c <= 'f') {
133            n *= base; n += c - 'a' + 10;
134        } else if('A' <=c && c <= 'F') {
135            n *= base; n += c - 'A' + 10;
136        } else
137            break;
138        c = FontFileGetc(f);
139    }
140
141    *cp = c; return n;
142}
143
144/* Skip to beginning of new line; return 1 if only whitespace was found. */
145static int
146endOfLine(FontFilePtr f, int c)
147{
148    if(c == 0)
149        c = FontFileGetc(f);
150
151    for(;;) {
152        if(c <= 0 || c == '\n')
153            return 1;
154        else if(c == '#') {
155            skipEndOfLine(f,c);
156            return 1;
157        }
158        else if(c == ' ' || c == '\t') {
159            skipEndOfLine(f,c);
160            return 0;
161        }
162        c = FontFileGetc(f);
163    }
164}
165
166/* Get a token; we're at first char */
167static int
168gettoken(FontFilePtr f, int c, int *cp)
169{
170    char *p;
171
172    if(c <= 0)
173    c = FontFileGetc(f);
174
175    if(c <= 0) {
176        return EOF_TOKEN;
177    }
178
179    while(c == ' ' || c == '\t')
180        c = FontFileGetc(f);
181
182    if(c=='\n') {
183        return EOL_TOKEN;
184    } else if(c == '#') {
185        skipEndOfLine(f,c);
186        return EOL_TOKEN;
187    } else if(c >= '0' && c <= '9') {
188        number_value = getnum(f,c,cp);
189        return NUMBER_TOKEN;
190    } else if((c >= 'A' && c <= 'Z') ||
191              (c >= 'a' && c <= 'z') ||
192              c == '/' || c == '_' || c == '-' || c == '.') {
193        p = keyword_value;
194        *p++ = c;
195        while(p-keyword_value < MAXKEYWORDLEN) {
196            c = FontFileGetc(f);
197            if(c <= ' ' || c > '~' || c == '#')
198                break;
199            *p++ = c;
200        }
201        *cp = c;
202        *p = '\0';
203        return KEYWORD_TOKEN;
204    } else {
205        *cp = c;
206        return ERROR_TOKEN;
207    }
208}
209
210/* Parse a line.
211 * Always skips to the beginning of a new line, even if an error occurs */
212static int
213getnextline(FontFilePtr f)
214{
215    int c, token;
216    c = FontFileGetc(f);
217    if(c <= 0)
218        return EOF_LINE;
219
220  again:
221    token=gettoken(f,c,&c);
222
223    switch(token) {
224    case EOF_TOKEN:
225        return EOF_LINE;
226    case EOL_TOKEN:
227        /* empty line */
228        c = FontFileGetc(f);
229        goto again;
230    case NUMBER_TOKEN:
231        value1 = number_value;
232        token = gettoken(f,c,&c);
233        switch(token) {
234        case NUMBER_TOKEN:
235            value2 = number_value;
236            token = gettoken(f,c,&c);
237            switch(token) {
238            case NUMBER_TOKEN:
239                value3 = number_value;
240                return CODE_RANGE_LINE;
241            case EOL_TOKEN:
242                return CODE_LINE;
243            default:
244                skipEndOfLine(f,c);
245                return ERROR_LINE;
246            }
247        case KEYWORD_TOKEN:
248            if(!endOfLine(f,c))
249                return ERROR_LINE;
250            else
251                return NAME_LINE;
252        default:
253            skipEndOfLine(f,c);
254            return ERROR_LINE;
255        }
256    case KEYWORD_TOKEN:
257        if(!strcasecmp(keyword_value, "STARTENCODING")) {
258            token = gettoken(f,c,&c);
259            if(token == KEYWORD_TOKEN) {
260                if(endOfLine(f,c))
261                    return STARTENCODING_LINE;
262                else
263                    return ERROR_LINE;
264            } else {
265                skipEndOfLine(f,c);
266                return ERROR_LINE;
267            }
268        } else if(!strcasecmp(keyword_value, "ALIAS")) {
269            token = gettoken(f,c,&c);
270            if(token == KEYWORD_TOKEN) {
271                if(endOfLine(f,c))
272                    return ALIAS_LINE;
273                else
274                    return ERROR_LINE;
275            } else {
276                skipEndOfLine(f,c);
277                return ERROR_LINE;
278            }
279        } else if(!strcasecmp(keyword_value, "SIZE")) {
280            token = gettoken(f,c,&c);
281            if(token == NUMBER_TOKEN) {
282                value1 = number_value;
283                token = gettoken(f,c,&c);
284                switch(token) {
285                case NUMBER_TOKEN:
286                    value2 = number_value;
287                    return SIZE_LINE;
288                case EOL_TOKEN:
289                    value2=0;
290                    return SIZE_LINE;
291                default:
292                    skipEndOfLine(f,c);
293                    return ERROR_LINE;
294                }
295            } else {
296                skipEndOfLine(f,c);
297                return ERROR_LINE;
298            }
299        } else if(!strcasecmp(keyword_value, "FIRSTINDEX")) {
300            token = gettoken(f,c,&c);
301            if(token == NUMBER_TOKEN) {
302                value1 = number_value;
303                token = gettoken(f,c,&c);
304                switch(token) {
305                case NUMBER_TOKEN:
306                    value2 = number_value;
307                    return FIRSTINDEX_LINE;
308                case EOL_TOKEN:
309                    value2 = 0;
310                    return FIRSTINDEX_LINE;
311                default:
312                    skipEndOfLine(f,c);
313                    return ERROR_LINE;
314                }
315            } else {
316                skipEndOfLine(f,c);
317                return ERROR_LINE;
318            }
319        } else if(!strcasecmp(keyword_value, "STARTMAPPING")) {
320            keyword_value[0] = 0;
321            value1 = 0; value1 = 0;
322            /* first a keyword */
323            token = gettoken(f,c,&c);
324            if(token != KEYWORD_TOKEN) {
325                skipEndOfLine(f, c);
326                return ERROR_LINE;
327            }
328
329            /* optional first integer */
330            token = gettoken(f,c,&c);
331            if(token == NUMBER_TOKEN) {
332                value1 = number_value;
333            } else if(token == EOL_TOKEN) {
334                return STARTMAPPING_LINE;
335            } else {
336                skipEndOfLine(f, c);
337                return ERROR_LINE;
338            }
339
340            /* optional second integer */
341            token = gettoken(f,c,&c);
342            if(token == NUMBER_TOKEN) {
343                value2 = number_value;
344            } else if(token == EOL_TOKEN) {
345                return STARTMAPPING_LINE;
346            } else {
347                skipEndOfLine(f, c);
348                return ERROR_LINE;
349            }
350
351            if(!endOfLine(f,c))
352                return ERROR_LINE;
353            else {
354                return STARTMAPPING_LINE;
355            }
356        } else if(!strcasecmp(keyword_value, "UNDEFINE")) {
357            /* first integer */
358            token = gettoken(f,c,&c);
359            if(token != NUMBER_TOKEN) {
360                skipEndOfLine(f,c);
361                return ERROR_LINE;
362            }
363            value1 = number_value;
364            /* optional second integer */
365            token = gettoken(f,c,&c);
366            if(token == EOL_TOKEN) {
367                value2 = value1;
368                return CODE_UNDEFINE_LINE;
369            } else if(token == NUMBER_TOKEN) {
370                value2 = number_value;
371                if(endOfLine(f,c)) {
372                    return CODE_UNDEFINE_LINE;
373                } else
374                    return ERROR_LINE;
375            } else {
376                skipEndOfLine(f,c);
377                return ERROR_LINE;
378            }
379        } else if(!strcasecmp(keyword_value, "ENDENCODING")) {
380            if(endOfLine(f,c))
381                return EOF_LINE;
382            else
383                return ERROR_LINE;
384        } else if(!strcasecmp(keyword_value, "ENDMAPPING")) {
385            if(endOfLine(f,c))
386                return ENDMAPPING_LINE;
387            else
388                return ERROR_LINE;
389        } else {
390            skipEndOfLine(f,c);
391            return ERROR_LINE;
392        }
393    default:
394        return ERROR_LINE;
395    }
396}
397
398static void
399install_mapping(FontEncPtr encoding, FontMapPtr mapping)
400{
401    FontMapPtr m;
402
403    if(encoding->mappings == NULL)
404        encoding->mappings = mapping;
405    else {
406        m = encoding->mappings;
407        while(m->next != NULL)
408            m = m->next;
409        m->next = mapping;
410    }
411    mapping->next = NULL;
412    mapping->encoding = encoding;
413}
414
415static int
416setCode(unsigned from, unsigned to, unsigned row_size,
417        unsigned *first, unsigned *last,
418        unsigned *encsize, unsigned short **enc)
419{
420    unsigned index, i;
421    unsigned short *newenc;
422
423    if(from>0xFFFF)
424        return 0;               /* success */
425
426    if(row_size==0)
427        index=from;
428    else {
429        if((value1 & 0xFF) >= row_size)
430            return 0;           /* ignore out of range mappings */
431        index = (from>>8) * row_size + (from&0xFF);
432    }
433
434    /* Optimize away useless identity mappings.  This is only expected
435       to be useful with linear encodings. */
436    if(index == to && (index < *first || index > *last))
437        return 0;
438    if(*encsize == 0) {
439        *encsize = (index < 256) ? 256 : 0x10000;
440        *enc = (unsigned short*)xalloc((*encsize) * sizeof(unsigned short));
441        if(*enc == NULL) {
442            *encsize = 0;
443            return 1;
444        }
445    } else if(*encsize <= index) {
446        *encsize = 0x10000;
447        if((newenc = (unsigned short*)xrealloc(enc, *encsize))==NULL)
448            return 1;
449        *enc = newenc;
450    }
451    if(*first > *last) {
452        *first = *last = index;
453    }
454    if(index < *first) {
455        for(i = index; i < *first; i++)
456            (*enc)[i] = i;
457        *first = index;
458    }
459    if(index > *last) {
460        for(i = *last + 1; i <= index; i++)
461            (*enc)[i] = i;
462        *last = index;
463    }
464    (*enc)[index] = to;
465    return 0;
466}
467
468/* Parser.  If headerOnly is true, we're only interested in the
469   data contained in the encoding file's header. */
470
471/* As font encodings are currently never freed, the allocations done
472   by this function are mostly its private business.  Note, however,
473   that FontEncIdentify needs to free the header fields -- so if you
474   change this function, you may need to change FontEncIdentify. */
475
476/* I want a garbage collector. */
477
478static FontEncPtr
479parseEncodingFile(FontFilePtr f, int headerOnly)
480{
481    int line;
482
483    unsigned short *enc=NULL;
484    char **nam = NULL, **newnam;
485    unsigned i, first = 0xFFFF, last=0, encsize=0, namsize=0;
486    FontEncPtr encoding = NULL;
487    FontMapPtr mapping = NULL;
488    FontEncSimpleMapPtr sm;
489    FontEncSimpleNamePtr sn;
490    char *aliases[MAXALIASES];
491    int numaliases=0;
492
493#if 0
494    /* GCC complains about unused labels.  Please fix GCC rather than
495       obfuscating my code. */
496  no_encoding:
497#endif
498    line = getnextline(f);
499    switch(line) {
500    case EOF_LINE:
501        goto error;
502    case STARTENCODING_LINE:
503        encoding = (FontEncPtr)xalloc(sizeof(FontEncRec));
504        if(encoding == NULL)
505            goto error;
506        encoding->name = (char*)xalloc(strlen(keyword_value)+1);
507        if(encoding->name == NULL)
508            goto error;
509        strcpy(encoding->name, keyword_value);
510        encoding->size = 256;
511        encoding->row_size = 0;
512        encoding->mappings = NULL;
513        encoding->next = NULL;
514        encoding->first = encoding->first_col=0;
515        goto no_mapping;
516    default:
517        goto error;
518    }
519
520  no_mapping:
521    line = getnextline(f);
522    switch(line) {
523    case EOF_LINE: goto done;
524    case ALIAS_LINE:
525        if(numaliases < MAXALIASES) {
526            aliases[numaliases] = (char*)xalloc(strlen(keyword_value)+1);
527            if(aliases[numaliases] == NULL)
528                goto error;
529            strcpy(aliases[numaliases], keyword_value);
530            numaliases++;
531        }
532        goto no_mapping;
533    case SIZE_LINE:
534        encoding->size = value1;
535        encoding->row_size = value2;
536        goto no_mapping;
537    case FIRSTINDEX_LINE:
538        encoding->first = value1;
539        encoding->first_col = value2;
540        goto no_mapping;
541    case STARTMAPPING_LINE:
542        if(headerOnly)
543            goto done;
544        if(!strcasecmp(keyword_value, "unicode")) {
545            mapping = (FontMapPtr)xalloc(sizeof(FontMapRec));
546            if(mapping == NULL)
547                goto error;
548            mapping->type = FONT_ENCODING_UNICODE;
549            mapping->pid = 0;
550            mapping->eid = 0;
551            mapping->recode = NULL;
552            mapping->name = NULL;
553            mapping->client_data = NULL;
554            mapping->next = NULL;
555            goto mapping;
556        } else if(!strcasecmp(keyword_value, "cmap")) {
557            mapping = (FontMapPtr)xalloc(sizeof(FontMapRec));
558            if(mapping == NULL)
559                goto error;
560            mapping->type = FONT_ENCODING_TRUETYPE;
561            mapping->pid = value1;
562            mapping->eid = value2;
563            mapping->recode = NULL;
564            mapping->name = NULL;
565            mapping->client_data = NULL;
566            mapping->next = NULL;
567            goto mapping;
568        } else if(!strcasecmp(keyword_value, "postscript")) {
569            mapping = (FontMapPtr)xalloc(sizeof(FontMapRec));
570            if(mapping == NULL)
571                goto error;
572            mapping->type = FONT_ENCODING_POSTSCRIPT;
573            mapping->pid = 0;
574            mapping->eid = 0;
575            mapping->recode = NULL;
576            mapping->name = NULL;
577            mapping->client_data = NULL;
578            mapping->next = NULL;
579            goto string_mapping;
580        } else {                /* unknown mapping type -- ignore */
581            goto skipmapping;
582        }
583        /* NOTREACHED */
584        goto error;
585    default: goto no_mapping;   /* ignore unknown lines */
586    }
587
588  skipmapping:
589    line = getnextline(f);
590    switch(line) {
591    case ENDMAPPING_LINE:
592        goto no_mapping;
593    case EOF_LINE:
594        goto error;
595    default:
596        goto skipmapping;
597    }
598
599  mapping:
600    line = getnextline(f);
601    switch(line) {
602    case EOF_LINE: goto error;
603    case ENDMAPPING_LINE:
604        mapping->recode = FontEncSimpleRecode;
605        mapping->name = FontEncUndefinedName;
606        mapping->client_data = sm =
607            (FontEncSimpleMapPtr)xalloc(sizeof(FontEncSimpleMapRec));
608        if(sm == NULL)
609            goto error;
610        sm->row_size = encoding->row_size;
611        if(first <= last) {
612            unsigned short *newmap;
613
614            sm->first = first;
615            sm->len=last-first+1;
616            newmap =
617                (unsigned short*)xalloc(sm->len * sizeof(unsigned short));
618            if(newmap == NULL) {
619                xfree(sm);
620                mapping->client_data = sm = NULL;
621                goto error;
622            }
623	    for(i=0; i < sm->len; i++)
624		newmap[i] = enc[first+i];
625	    sm->map = newmap;
626        } else {
627            sm->first = 0;
628            sm->len = 0;
629            sm->map = NULL;
630        }
631        install_mapping(encoding, mapping);
632        mapping = NULL;
633        first = 0xFFFF; last=0;
634        goto no_mapping;
635
636    case CODE_LINE:
637        if(setCode(value1, value2, encoding->row_size,
638                   &first, &last, &encsize, &enc))
639            goto error;
640        goto mapping;
641
642    case CODE_RANGE_LINE:
643        if(value1 > 0x10000)
644            value1 = 0x10000;
645        if(value2 > 0x10000)
646            value2 = 0x10000;
647        if(value2 < value1)
648            goto mapping;
649        /* Do the last value first to avoid having to realloc() */
650        if(setCode(value2, value3+(value2-value1), encoding->row_size,
651                   &first, &last, &encsize, &enc))
652            goto error;
653        for(i=value1; i<value2; i++) {
654            if(setCode(i, value3+(i-value1), encoding->row_size,
655                       &first, &last, &encsize, &enc))
656                goto error;
657        }
658        goto mapping;
659
660    case CODE_UNDEFINE_LINE:
661        if(value1 > 0x10000)
662            value1 = 0x10000;
663        if(value2 > 0x10000)
664            value2 = 0x10000;
665        if(value2 < value1)
666            goto mapping;
667        /* Do the last value first to avoid having to realloc() */
668        if(setCode(value2, 0, encoding->row_size,
669                   &first, &last, &encsize, &enc))
670            goto error;
671        for(i = value1; i < value2; i++) {
672            if(setCode(i, 0, encoding->row_size,
673                       &first, &last, &encsize, &enc))
674                goto error;
675        }
676        goto mapping;
677
678    default: goto mapping;      /* ignore unknown lines */
679    }
680
681  string_mapping:
682    line = getnextline(f);
683    switch(line) {
684    case EOF_LINE: goto error;
685    case ENDMAPPING_LINE:
686        mapping->recode = FontEncUndefinedRecode;
687        mapping->name = FontEncSimpleName;
688        mapping->client_data = sn =
689            (FontEncSimpleNamePtr)xalloc(sizeof(FontEncSimpleNameRec));
690        if(sn == NULL)
691            goto error;
692        if(first > last) {
693            xfree(sn);
694            mapping->client_data = sn = NULL;
695            goto error;
696        }
697        sn->first = first;
698        sn->len = last - first + 1;
699        sn->map = (char**)xalloc(sn->len*sizeof(char*));
700        if(sn->map == NULL) {
701            xfree(sn);
702            mapping->client_data = sn = NULL;
703            goto error;
704        }
705        for(i = 0; i < sn->len; i++)
706            sn->map[i] = nam[first+i];
707        install_mapping(encoding,mapping);
708        mapping = NULL;
709        first = 0xFFFF; last=0;
710        goto no_mapping;
711    case NAME_LINE:
712        if(value1 >= 0x10000) goto string_mapping;
713        if(namsize == 0) {
714            namsize = (value1) < 256 ? 256 : 0x10000;
715            nam = (char**)xalloc(namsize * sizeof(char*));
716            if(nam == NULL) {
717                namsize=0;
718                goto error;
719            }
720        } else if(namsize <= value1) {
721            namsize = 0x10000;
722            if((newnam = (char**)xrealloc(nam, namsize)) == NULL)
723                goto error;
724            nam = newnam;
725        }
726        if(first > last) {
727            first = last = value1;
728        }
729        if(value1 < first) {
730            for(i = value1; i < first; i++)
731                nam[i] = NULL;
732            first = value1;
733        }
734        if(value1 > last) {
735            for(i=last+1; i <= value1; i++)
736                nam[i]=NULL;
737            last = value1;
738        }
739        nam[value1] = (char*)xalloc(strlen(keyword_value)+1);
740        if(nam[value1] == NULL) {
741            goto error;
742        }
743        strcpy(nam[value1], keyword_value);
744        goto string_mapping;
745
746    default: goto string_mapping; /* ignore unknown lines */
747    }
748
749  done:
750    if(encsize) xfree(enc); encsize=0; enc = NULL;
751    if(namsize) xfree(nam); namsize=0; nam = NULL; /* don't free entries! */
752
753    encoding->aliases=NULL;
754    if(numaliases) {
755        encoding->aliases = (char**)xalloc((numaliases+1)*sizeof(char*));
756        if(encoding->aliases == NULL)
757            goto error;
758        for(i=0; i<numaliases; i++)
759            encoding->aliases[i] = aliases[i];
760        encoding->aliases[numaliases]=NULL;
761    }
762
763    return encoding;
764
765error:
766    if(encsize) xfree(enc); encsize=0;
767    if(namsize) {
768        for(i = first; i <= last; i++)
769            if(nam[i])
770                xfree(nam[i]);
771        xfree(nam);
772        namsize = 0;
773    }
774    if(mapping) {
775        if(mapping->client_data) xfree(mapping->client_data);
776        xfree(mapping);
777    }
778    if(encoding) {
779	FontMapPtr nextmap;
780	if (encoding->name) xfree(encoding->name);
781	for (mapping = encoding->mappings; mapping; mapping = nextmap) {
782	    if (mapping->client_data) xfree(mapping->client_data);
783	    nextmap = mapping->next;
784	    xfree(mapping);
785	}
786	xfree(encoding);
787    }
788    for(i = 0; i < numaliases; i++)
789        xfree(aliases[i]);
790    /* We don't need to free sn and sm as they handled locally in the body.*/
791    return NULL;
792}
793
794char*
795FontEncDirectory(void)
796{
797    static char* dir = NULL;
798
799    if(dir == NULL) {
800        char *c = getenv("FONT_ENCODINGS_DIRECTORY");
801        if(c) {
802            dir = malloc(strlen(c) + 1);
803            if(!dir)
804                return NULL;
805            strcpy(dir, c);
806        } else {
807            dir = FONT_ENCODINGS_DIRECTORY;
808        }
809    }
810    return dir;
811}
812
813static void
814parseFontFileName(const char *fontFileName, char *buf, char *dir)
815{
816    const char *p;
817    char *q, *lastslash;
818
819    for(p = fontFileName, q = dir, lastslash = NULL; *p; p++, q++) {
820        *q = *p;
821        if(*p == '/')
822            lastslash = q+1;
823    }
824
825    if(!lastslash)
826        lastslash = dir;
827
828    *lastslash = '\0';
829
830    if(buf && strlen(dir) + 14 < MAXFONTFILENAMELEN) {
831        strcpy(buf, dir);
832        strcat(buf, "encodings.dir");
833    }
834}
835
836static FontEncPtr
837FontEncReallyReallyLoad(const char *charset,
838                        const char *dirname, const char *dir)
839{
840    FontFilePtr f;
841    FILE *file;
842    FontEncPtr encoding;
843    char file_name[MAXFONTFILENAMELEN], encoding_name[MAXFONTNAMELEN],
844        buf[MAXFONTFILENAMELEN];
845    int count, n;
846    static char format[24] = "";
847
848    /* As we don't really expect to open encodings that often, we don't
849       take the trouble of caching encodings directories. */
850
851    if((file = fopen(dirname, "r")) == NULL) {
852        return NULL;
853    }
854
855    count = fscanf(file, "%d\n", &n);
856    if(count == EOF || count != 1) {
857        fclose(file);
858        return NULL;
859    }
860
861    encoding = NULL;
862    if (!format[0]) {
863	sprintf(format, "%%%ds %%%d[^\n]\n", (int)sizeof(encoding_name) - 1,
864		(int)sizeof(file_name) - 1);
865    }
866    for(;;) {
867        count = fscanf(file, format, encoding_name, file_name);
868        if(count == EOF)
869            break;
870        if(count != 2)
871            break;
872
873        if(!strcasecmp(encoding_name, charset)) {
874            /* Found it */
875            if(file_name[0] != '/') {
876                if(strlen(dir) + strlen(file_name) >= MAXFONTFILENAMELEN) {
877		    fclose(file);
878                    return NULL;
879		}
880                strcpy(buf, dir);
881                strcat(buf, file_name);
882            } else {
883                strcpy(buf , file_name);
884            }
885
886            f = FontFileOpen(buf);
887            if(f == NULL) {
888		fclose(file);
889                return NULL;
890            }
891            encoding = parseEncodingFile(f, 0);
892            FontFileClose(f);
893            break;
894        }
895    }
896
897    fclose(file);
898
899    return encoding;
900}
901
902/* Parser ntrypoint -- used by FontEncLoad */
903FontEncPtr
904FontEncReallyLoad(const char *charset, const char *fontFileName)
905{
906    FontEncPtr encoding;
907    char dir[MAXFONTFILENAMELEN], dirname[MAXFONTFILENAMELEN];
908    char *d;
909
910    if(fontFileName) {
911        parseFontFileName(fontFileName, dirname, dir);
912        encoding = FontEncReallyReallyLoad(charset, dirname, dir);
913        if(encoding)
914            return(encoding);
915    }
916
917    d = FontEncDirectory();
918    if(d) {
919        parseFontFileName(d, NULL, dir);
920        encoding = FontEncReallyReallyLoad(charset, d, dir);
921        return encoding;
922    }
923
924    return NULL;
925}
926
927/* Return a NULL-terminated array of encoding names.  Note that this
928 * function has incestuous knowledge of the allocations done by
929 * parseEncodingFile. */
930
931char **
932FontEncIdentify(const char *fileName)
933{
934    FontFilePtr f;
935    FontEncPtr encoding;
936    char **names, **name, **alias;
937    int numaliases;
938
939    if((f = FontFileOpen(fileName))==NULL) {
940        return NULL;
941    }
942    encoding = parseEncodingFile(f, 1);
943    FontFileClose(f);
944
945    if(!encoding)
946        return NULL;
947
948    numaliases = 0;
949    if(encoding->aliases)
950        for(alias = encoding->aliases; *alias; alias++)
951            numaliases++;
952
953    names = (char**)xalloc((numaliases+2)*sizeof(char*));
954    if(names == NULL) {
955        if(encoding->aliases)
956            xfree(encoding->aliases);
957        xfree(encoding);
958        return NULL;
959    }
960
961    name = names;
962    *(name++) = encoding->name;
963    if(numaliases > 0)
964    for(alias = encoding->aliases; *alias; alias++, name++)
965        *name = *alias;
966
967    *name = NULL;
968    xfree(encoding->aliases);
969    xfree(encoding);
970
971    return names;
972}
973