ident.c revision 7c5a9b20
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. All rights reserved.
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    size_t n = strlen(filename);
85
86    if (n > 4 && strcmp(filename + n - 4, ".bz2") == 0) {
87	ff->type = bz2FontFile;
88	ff->f.bz2 = BZ2_bzopen(filename, "rb");
89	ff->pos = 0;
90	return ff->f.bz2;
91    } else {
92	ff->type = gzFontFile;
93	ff->f.gz = gzopen(filename, "rb");
94	return ff->f.gz;
95    }
96}
97
98static inline int
99fontFileRead(fontFile *ff, void *buf, unsigned len)
100{
101    if (ff->type == gzFontFile) {
102	return gzread(ff->f.gz, buf, len);
103    } else {
104	int r = BZ2_bzread(ff->f.bz2, buf, len);
105	ff->pos += r;
106	return r;
107    }
108}
109
110static inline int
111fontFileGetc(fontFile *ff)
112{
113    if (ff->type == gzFontFile) {
114	return gzgetc(ff->f.gz);
115    } else {
116	char buf;
117	if (BZ2_bzread(ff->f.bz2, &buf, 1) != 1) {
118	    return -1;
119	} else {
120	    ff->pos += 1;
121	    return (int) buf;
122	}
123    }
124}
125
126static long
127fontFileSeek(fontFile *ff, z_off_t offset, int whence)
128{
129    if (ff->type == gzFontFile) {
130	return gzseek(ff->f.gz, offset, whence);
131    } else {
132	/* bzlib has no easy equivalent so we have to fake it,
133	 * fortunately, we only have to handle a couple of cases
134	 */
135	z_off_t n;
136	char buf[BUFSIZ];
137
138	switch (whence) {
139	  case SEEK_SET:
140	    n = offset - ff->pos;
141	    break;
142	  case SEEK_CUR:
143	    n = offset;
144	    break;
145	  default:
146	    return -1;
147	}
148
149	while (n > BUFSIZ) {
150	    if (BZ2_bzread(ff->f.bz2, buf, BUFSIZ) != BUFSIZ)
151		return -1;
152	    n -= BUFSIZ;
153	}
154	if (BZ2_bzread(ff->f.bz2, buf, (int) n) != n)
155	    return -1;
156	ff->pos = offset;
157	return offset;
158    }
159}
160
161
162static inline int
163fontFileClose(fontFile *ff)
164{
165    if (ff->type == gzFontFile) {
166	return gzclose(ff->f.gz);
167    } else {
168	BZ2_bzclose(ff->f.bz2);
169	return 0;
170    }
171}
172
173#else /* no bzip2, only gzip */
174typedef gzFile fontFile;
175# define fontFileOpen(ff, filename)	(*(ff) = gzopen(filename, "rb"))
176# define fontFileRead(ff, buf, len)	gzread(*(ff), buf, len)
177# define fontFileGetc(ff)		gzgetc(*(ff))
178# define fontFileSeek(ff, off, whence)	gzseek(*(ff), off, whence)
179# define fontFileClose(ff)		gzclose(*(ff))
180#endif
181
182static int pcfIdentify(fontFile *f, char **name);
183static int bdfIdentify(fontFile *f, char **name);
184
185static int
186getLSB32(fontFile *f)
187{
188    int rc;
189    unsigned char c[4];
190
191    rc = fontFileRead(f, c, 4);
192    if(rc != 4)
193        return -1;
194    return (c[0]) | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
195}
196
197static int
198getInt8(fontFile *f, int format)
199{
200    unsigned char c;
201    int rc;
202
203    rc = fontFileRead(f, &c, 1);
204    if(rc != 1)
205        return -1;
206    return c;
207}
208
209static int
210getInt32(fontFile *f, int format)
211{
212    int rc;
213    unsigned char c[4];
214
215    rc = fontFileRead(f, c, 4);
216    if(rc != 4)
217        return -1;
218
219    if(format & (1 << 2)) {
220        return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | (c[3]);
221    } else {
222        return (c[0]) | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
223    }
224}
225
226int
227bitmapIdentify(const char *filename, char **name)
228{
229    fontFile ff;
230    int magic;
231
232    if (fontFileOpen(&ff, filename) == NULL)
233	return -1;
234
235    magic = getLSB32(&ff);
236    if(magic == PCF_VERSION)
237        return pcfIdentify(&ff, name);
238    else if(magic == ('S' | ('T' << 8) | ('A' << 16) | ('R') << 24))
239        return bdfIdentify(&ff, name);
240
241    fontFileClose(&ff);
242    return 0;
243}
244
245static int
246pcfIdentify(fontFile *f, char **name)
247{
248    int prop_position;
249    PropPtr props = NULL;
250    int format, count, nprops, i, string_size;
251    long rc;
252    char *strings = NULL, *s;
253
254    count = getLSB32(f);
255    if(count <= 0)
256        goto fail;
257
258    prop_position = -1;
259    for(i = 0; i < count; i++) {
260        int type, offset;
261        type = getLSB32(f);
262        (void) getLSB32(f);
263        (void) getLSB32(f);
264        offset = getLSB32(f);
265        if(type == PCF_PROPERTIES) {
266            prop_position = offset;
267            break;
268        }
269    }
270    if(prop_position < 0)
271        goto fail;
272
273    rc = fontFileSeek(f, prop_position, SEEK_SET);
274    if(rc < 0)
275        goto fail;
276
277    format = getLSB32(f);
278    if((format & 0xFFFFFF00) != 0)
279        goto fail;
280    nprops = getInt32(f, format);
281    if(nprops <= 0 || nprops > 1000)
282        goto fail;
283    props = malloc(nprops * sizeof(PropRec));
284    if(props == NULL)
285        goto fail;
286
287    for(i = 0; i < nprops; i++) {
288        props[i].name = getInt32(f, format);
289        props[i].isString = getInt8(f, format);
290        props[i].value = getInt32(f, format);
291    }
292    if(nprops & 3) {
293	rc = fontFileSeek(f, 4 - (nprops & 3), SEEK_CUR);
294        if(rc < 0)
295            goto fail;
296    }
297
298    string_size = getInt32(f, format);
299    if(string_size < 0 || string_size > 100000)
300        goto fail;
301    strings = malloc(string_size);
302    if(!strings)
303        goto fail;
304
305    rc = fontFileRead(f, strings, string_size);
306    if(rc != string_size)
307        goto fail;
308
309    for(i = 0; i < nprops; i++) {
310        if(!props[i].isString ||
311           props[i].name >= string_size - 4 ||
312           props[i].value >= string_size)
313            continue;
314        if(strcmp(strings + props[i].name, "FONT") == 0)
315            break;
316    }
317
318    if(i >= nprops)
319        goto fail;
320
321    s = strdup(strings + props[i].value);
322    if(s == NULL)
323        goto fail;
324    *name = s;
325    free(strings);
326    free(props);
327    fontFileClose(f);
328    return 1;
329
330 fail:
331    if(strings) free(strings);
332    if(props) free(props);
333    fontFileClose(f);
334    return 0;
335}
336
337#define NKEY 20
338
339static char*
340getKeyword(fontFile *f, int *eol)
341{
342    static char keyword[NKEY + 1];
343    int c, i;
344    i = 0;
345    while(i < NKEY) {
346        c = fontFileGetc(f);
347        if(c == ' ' || c == '\n') {
348            if(i <= 0)
349                return NULL;
350            if(eol)
351                *eol = (c == '\n');
352            keyword[i] = '\0';
353            return keyword;
354        }
355        if(c < 'A' || c > 'Z')
356            return NULL;
357        keyword[i++] = c;
358    }
359    return NULL;
360}
361
362static int
363bdfskip(fontFile *f)
364{
365    int c;
366    do {
367        c = fontFileGetc(f);
368    } while(c >= 0 && c != '\n');
369    if(c < 0)
370        return -1;
371    return 1;
372}
373
374static char *
375bdfend(fontFile *f)
376{
377    int c;
378    char *buf = NULL;
379    int bufsize = 0;
380    int i = 0;
381
382    do {
383        c = fontFileGetc(f);
384    } while (c == ' ');
385
386    while(i < 1000) {
387        if(c < 0 || (c == '\n' && i == 0)) {
388            goto fail;
389        }
390        if(bufsize < i + 1) {
391            char *newbuf;
392            if(bufsize == 0) {
393                bufsize = 20;
394                newbuf = malloc(bufsize);
395            } else {
396                bufsize = 2 * bufsize;
397                newbuf = realloc(buf, bufsize);
398            }
399            if(newbuf == NULL)
400                goto fail;
401            buf = newbuf;
402        }
403        if(c == '\n') {
404            buf[i] = '\0';
405            return buf;
406        }
407        buf[i++] = c;
408        c = fontFileGetc(f);
409    }
410
411 fail:
412    if(buf)
413        free(buf);
414    return NULL;
415}
416
417static int
418bdfIdentify(fontFile *f, char **name)
419{
420    char *k;
421    int rc;
422    int eol;
423    /* bitmapIdentify already read "STAR", so we need to check for
424       "TFONT" */
425    k = getKeyword(f, &eol);
426    if(k == NULL || eol)
427        goto fail;
428    if(strcmp(k, "TFONT") != 0)
429        goto fail;
430    while(1) {
431        if(!eol) {
432            rc = bdfskip(f);
433            if(rc < 0)
434                goto fail;
435        }
436        k = getKeyword(f, &eol);
437        if(k == NULL)
438            goto fail;
439        else if(strcmp(k, "FONT") == 0) {
440            if(eol)
441                goto fail;
442            k = bdfend(f);
443            if(k == NULL)
444                goto fail;
445            *name = k;
446            fontFileClose(f);
447            return 1;
448        } else if(strcmp(k, "CHARS") == 0)
449            goto fail;
450    }
451 fail:
452    fontFileClose(f);
453    return 0;
454}
455