ident.c revision 6ae2c069
1/*
2  Copyright (c) 2003 by Juliusz Chroboczek
3
4  Permission is hereby granted, free of charge, to any person obtaining a copy
5  of this software and associated documentation files (the "Software"), to deal
6  in the Software without restriction, including without limitation the rights
7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  copies of the Software, and to permit persons to whom the Software is
9  furnished to do so, subject to the following conditions:
10
11  The above copyright notice and this permission notice shall be included in
12  all copies or substantial portions of the Software.
13
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  THE SOFTWARE.
21*/
22/*
23 * Copyright (c) 2008, Oracle and/or its affiliates.
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a
26 * copy of this software and associated documentation files (the "Software"),
27 * to deal in the Software without restriction, including without limitation
28 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
29 * and/or sell copies of the Software, and to permit persons to whom the
30 * Software is furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice (including the next
33 * paragraph) shall be included in all copies or substantial portions of the
34 * Software.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
39 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
41 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
42 * DEALINGS IN THE SOFTWARE.
43 */
44
45/* The function identifyBitmap returns -1 if filename is definitively not
46   a font file, 1 if it is a single-face bitmap font with a XLFD name,
47   and 0 if it should be processed normally.  identifyBitmap is
48   much faster than parsing the whole font. */
49
50#ifdef HAVE_CONFIG_H
51#include "config.h"
52#endif
53
54#include <stdlib.h>
55#include <string.h>
56#include "zlib.h"
57#include "ident.h"
58
59#ifdef X_BZIP2_FONT_COMPRESSION
60#include <bzlib.h>
61#endif
62
63#define PCF_VERSION (('p'<<24)|('c'<<16)|('f'<<8)|1)
64#define PCF_PROPERTIES (1 << 0)
65
66typedef struct _Prop {
67    unsigned name;
68    int isString;
69    unsigned value;
70} PropRec, *PropPtr;
71
72#ifdef X_BZIP2_FONT_COMPRESSION
73typedef struct {
74    enum { gzFontFile, bz2FontFile } type;
75    union {
76        gzFile gz;
77        BZFILE *bz2;
78    } f;
79    unsigned long pos;
80} fontFile;
81
82static inline void *
83fontFileOpen(fontFile *ff, const char *filename)
84{
85    size_t n = strlen(filename);
86
87    if (n > 4 && strcmp(filename + n - 4, ".bz2") == 0) {
88        ff->type = bz2FontFile;
89        ff->f.bz2 = BZ2_bzopen(filename, "rb");
90        ff->pos = 0;
91        return ff->f.bz2;
92    }
93    else {
94        ff->type = gzFontFile;
95        ff->f.gz = gzopen(filename, "rb");
96        return ff->f.gz;
97    }
98}
99
100static inline int
101fontFileRead(fontFile *ff, void *buf, unsigned len)
102{
103    if (ff->type == gzFontFile) {
104        return gzread(ff->f.gz, buf, len);
105    }
106    else {
107        int r = BZ2_bzread(ff->f.bz2, buf, len);
108
109        ff->pos += r;
110        return r;
111    }
112}
113
114static inline int
115fontFileGetc(fontFile *ff)
116{
117    if (ff->type == gzFontFile) {
118        return gzgetc(ff->f.gz);
119    }
120    else {
121        char buf;
122
123        if (BZ2_bzread(ff->f.bz2, &buf, 1) != 1) {
124            return -1;
125        }
126        else {
127            ff->pos += 1;
128            return (int) buf;
129        }
130    }
131}
132
133static long
134fontFileSeek(fontFile *ff, z_off_t offset, int whence)
135{
136    if (ff->type == gzFontFile) {
137        return gzseek(ff->f.gz, offset, whence);
138    }
139    else {
140        /* bzlib has no easy equivalent so we have to fake it,
141         * fortunately, we only have to handle a couple of cases
142         */
143        z_off_t n;
144        char buf[BUFSIZ];
145
146        switch (whence) {
147        case SEEK_SET:
148            n = offset - ff->pos;
149            break;
150        case SEEK_CUR:
151            n = offset;
152            break;
153        default:
154            return -1;
155        }
156
157        while (n > BUFSIZ) {
158            if (BZ2_bzread(ff->f.bz2, buf, BUFSIZ) != BUFSIZ)
159                return -1;
160            n -= BUFSIZ;
161        }
162        if (BZ2_bzread(ff->f.bz2, buf, (int) n) != n)
163            return -1;
164        ff->pos = offset;
165        return offset;
166    }
167}
168
169static inline int
170fontFileClose(fontFile *ff)
171{
172    if (ff->type == gzFontFile) {
173        return gzclose(ff->f.gz);
174    }
175    else {
176        BZ2_bzclose(ff->f.bz2);
177        return 0;
178    }
179}
180
181#else                           /* no bzip2, only gzip */
182typedef gzFile fontFile;
183
184#define fontFileOpen(ff, filename)	(*(ff) = gzopen(filename, "rb"))
185#define fontFileRead(ff, buf, len)	gzread(*(ff), buf, len)
186#define fontFileGetc(ff)		gzgetc(*(ff))
187#define fontFileSeek(ff, off, whence)	gzseek(*(ff), off, whence)
188#define fontFileClose(ff)		gzclose(*(ff))
189#endif
190
191static int pcfIdentify(fontFile *f, char **name);
192static int bdfIdentify(fontFile *f, char **name);
193
194static int
195getLSB32(fontFile *f)
196{
197    int rc;
198    unsigned char c[4];
199
200    rc = fontFileRead(f, c, 4);
201    if (rc != 4)
202        return -1;
203    return (c[0]) | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
204}
205
206static int
207getInt8(fontFile *f, int format)
208{
209    unsigned char c;
210    int rc;
211
212    rc = fontFileRead(f, &c, 1);
213    if (rc != 1)
214        return -1;
215    return c;
216}
217
218static int
219getInt32(fontFile *f, int format)
220{
221    int rc;
222    unsigned char c[4];
223
224    rc = fontFileRead(f, c, 4);
225    if (rc != 4)
226        return -1;
227    else {
228        unsigned int u[4] = { c[0], c[1], c[2], c[3] };
229
230        if (format & (1 << 2)) {
231            return (int) ((u[0] << 24) | (u[1] << 16) | (u[2] << 8) | (u[3]));
232        }
233        else {
234            return (int) ((u[0]) | (u[1] << 8) | (u[2] << 16) | (u[3] << 24));
235        }
236    }
237}
238
239int
240bitmapIdentify(const char *filename, char **name)
241{
242    fontFile ff;
243    int magic;
244
245    if (fontFileOpen(&ff, filename) == NULL)
246        return -1;
247
248    magic = getLSB32(&ff);
249    if (magic == PCF_VERSION)
250        return pcfIdentify(&ff, name);
251    else if (magic == ('S' | ('T' << 8) | ('A' << 16) | ('R') << 24))
252        return bdfIdentify(&ff, name);
253
254    fontFileClose(&ff);
255    return 0;
256}
257
258static int
259pcfIdentify(fontFile *f, char **name)
260{
261    int prop_position;
262    PropPtr props = NULL;
263    int format, count, nprops, i, string_size;
264    long rc;
265    char *strings = NULL, *s;
266
267    count = getLSB32(f);
268    if (count <= 0)
269        goto fail;
270
271    prop_position = -1;
272    for (i = 0; i < count; i++) {
273        int type, offset;
274
275        type = getLSB32(f);
276        (void) getLSB32(f);
277        (void) getLSB32(f);
278        offset = getLSB32(f);
279        if (type == PCF_PROPERTIES) {
280            prop_position = offset;
281            break;
282        }
283    }
284    if (prop_position < 0)
285        goto fail;
286
287    rc = fontFileSeek(f, prop_position, SEEK_SET);
288    if (rc < 0)
289        goto fail;
290
291    format = getLSB32(f);
292    if ((format & 0xFFFFFF00) != 0)
293        goto fail;
294    nprops = getInt32(f, format);
295    if (nprops <= 0 || nprops > 1000)
296        goto fail;
297    props = malloc(nprops * sizeof(PropRec));
298    if (props == NULL)
299        goto fail;
300
301    for (i = 0; i < nprops; i++) {
302        props[i].name = getInt32(f, format);
303        props[i].isString = getInt8(f, format);
304        props[i].value = getInt32(f, format);
305    }
306    if (nprops & 3) {
307        rc = fontFileSeek(f, 4 - (nprops & 3), SEEK_CUR);
308        if (rc < 0)
309            goto fail;
310    }
311
312    string_size = getInt32(f, format);
313    if (string_size < 0 || string_size > 100000)
314        goto fail;
315    strings = malloc(string_size);
316    if (!strings)
317        goto fail;
318
319    rc = fontFileRead(f, strings, string_size);
320    if (rc != string_size)
321        goto fail;
322
323    for (i = 0; i < nprops; i++) {
324        if (!props[i].isString ||
325            props[i].name >= string_size - 4 || props[i].value >= string_size)
326            continue;
327        if (strcmp(strings + props[i].name, "FONT") == 0)
328            break;
329    }
330
331    if (i >= nprops)
332        goto fail;
333
334    s = strdup(strings + props[i].value);
335    if (s == NULL)
336        goto fail;
337    *name = s;
338    free(strings);
339    free(props);
340    fontFileClose(f);
341    return 1;
342
343 fail:
344    if (strings)
345        free(strings);
346    if (props)
347        free(props);
348    fontFileClose(f);
349    return 0;
350}
351
352#define NKEY 20
353
354static char *
355getKeyword(fontFile *f, int *eol)
356{
357    static char keyword[NKEY + 1];
358    int i = 0;
359
360    while (i < NKEY) {
361        int c = fontFileGetc(f);
362        if (c == ' ' || c == '\n') {
363            if (i <= 0)
364                return NULL;
365            if (eol)
366                *eol = (c == '\n');
367            keyword[i] = '\0';
368            return keyword;
369        }
370        if (c < 'A' || c > 'Z')
371            return NULL;
372        keyword[i++] = c;
373    }
374    return NULL;
375}
376
377static int
378bdfskip(fontFile *f)
379{
380    int c;
381
382    do {
383        c = fontFileGetc(f);
384    } while (c >= 0 && c != '\n');
385    if (c < 0)
386        return -1;
387    return 1;
388}
389
390static char *
391bdfend(fontFile *f)
392{
393    int c;
394    char *buf = NULL;
395    size_t bufsize = 0;
396    unsigned int i = 0;
397
398    do {
399        c = fontFileGetc(f);
400    } while (c == ' ');
401
402    while (i < 1000) {
403        if (c < 0 || (c == '\n' && i == 0)) {
404            goto fail;
405        }
406        if (bufsize < i + 1) {
407            char *newbuf;
408
409            if (bufsize == 0) {
410                bufsize = 20;
411                newbuf = malloc(bufsize);
412            }
413            else {
414                bufsize = 2 * bufsize;
415                newbuf = realloc(buf, bufsize);
416            }
417            if (newbuf == NULL)
418                goto fail;
419            buf = newbuf;
420        }
421        if (c == '\n') {
422            buf[i] = '\0';
423            return buf;
424        }
425        buf[i++] = c;
426        c = fontFileGetc(f);
427    }
428
429 fail:
430    if (buf)
431        free(buf);
432    return NULL;
433}
434
435static int
436bdfIdentify(fontFile *f, char **name)
437{
438    char *k;
439    int eol;
440
441    /* bitmapIdentify already read "STAR", so we need to check for
442       "TFONT" */
443    k = getKeyword(f, &eol);
444    if (k == NULL || eol)
445        goto fail;
446    if (strcmp(k, "TFONT") != 0)
447        goto fail;
448    while (1) {
449        if (!eol) {
450            int rc = bdfskip(f);
451            if (rc < 0)
452                goto fail;
453        }
454        k = getKeyword(f, &eol);
455        if (k == NULL)
456            goto fail;
457        else if (strcmp(k, "FONT") == 0) {
458            if (eol)
459                goto fail;
460            k = bdfend(f);
461            if (k == NULL)
462                goto fail;
463            *name = k;
464            fontFileClose(f);
465            return 1;
466        }
467        else if (strcmp(k, "CHARS") == 0)
468            goto fail;
469    }
470 fail:
471    fontFileClose(f);
472    return 0;
473}
474