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