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