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