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