1/*
2
3Copyright 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
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
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27/*
28 * This file contains miscellaneous utility routines and is not part of the
29 * Xlib standard.
30 *
31 * Public entry points:
32 *
33 *     XmuReadBitmapData		read data from FILE descriptor
34 *     XmuReadBitmapDataFromFile	read X10 or X11 format bitmap files
35 *					and return data
36 *
37 * Note that this file and ../X/XRdBitF.c look very similar....  Keep them
38 * that way (but don't use common source code so that people can have one
39 * without the other).
40 */
41
42
43/*
44 * Based on an optimized version provided by Jim Becker, August 5, 1988.
45 */
46
47#ifdef HAVE_CONFIG_H
48#include <config.h>
49#endif
50#include <X11/Xos.h>
51#include <X11/Xlib.h>
52#include <X11/Xutil.h>
53#include <X11/Xlibint.h>
54#include <stdio.h>
55#include <ctype.h>
56#include <X11/Xmu/Drawing.h>
57#ifdef WIN32
58#include <X11/Xwindows.h>
59#include <direct.h>            /* for _getdrives() */
60#endif
61
62#define MAX_SIZE 255
63
64/*
65 * Prototypes
66 */
67static void initHexTable(void);
68static int NextInt(FILE*);
69
70/* shared data for the image read/parse logic */
71static short hexTable[256];		/* conversion value */
72static Bool initialized = False;	/* easier to fill in at run time */
73
74
75/*
76 *	Table index for the hex values. Initialized once, first time.
77 *	Used for translation value or delimiter significance lookup.
78 */
79static void
80initHexTable(void)
81{
82    /*
83     * We build the table at run time for several reasons:
84     *
85     *     1.  portable to non-ASCII machines.
86     *     2.  still reentrant since we set the init flag after setting table.
87     *     3.  easier to extend.
88     *     4.  less prone to bugs.
89     */
90    hexTable['0'] = 0;	hexTable['1'] = 1;
91    hexTable['2'] = 2;	hexTable['3'] = 3;
92    hexTable['4'] = 4;	hexTable['5'] = 5;
93    hexTable['6'] = 6;	hexTable['7'] = 7;
94    hexTable['8'] = 8;	hexTable['9'] = 9;
95    hexTable['A'] = 10;	hexTable['B'] = 11;
96    hexTable['C'] = 12;	hexTable['D'] = 13;
97    hexTable['E'] = 14;	hexTable['F'] = 15;
98    hexTable['a'] = 10;	hexTable['b'] = 11;
99    hexTable['c'] = 12;	hexTable['d'] = 13;
100    hexTable['e'] = 14;	hexTable['f'] = 15;
101
102    /* delimiters of significance are flagged w/ negative value */
103    hexTable[' '] = -1;	hexTable[','] = -1;
104    hexTable['}'] = -1;	hexTable['\n'] = -1;
105    hexTable['\t'] = -1;
106
107    initialized = True;
108}
109
110/*
111 *	read next hex value in the input stream, return -1 if EOF
112 */
113static int
114NextInt(FILE *fstream)
115{
116    int	ch;
117    int	value = 0;
118    int gotone = 0;
119    int done = 0;
120
121    /* loop, accumulate hex value until find delimiter  */
122    /* skip any initial delimiters found in read stream */
123
124    while (!done) {
125	ch = getc(fstream);
126	if (ch == EOF) {
127	    value	= -1;
128	    done++;
129	} else {
130	    /* trim high bits, check type and accumulate */
131	    ch &= 0xff;
132	    if (isascii(ch) && isxdigit(ch)) {
133		value = (value << 4) + hexTable[ch];
134		gotone++;
135	    } else if ((hexTable[ch]) < 0 && gotone)
136	      done++;
137	}
138    }
139    return value;
140}
141
142
143/*
144 * The data returned by the following routine is always in left-most byte
145 * first and left-most bit first.  If it doesn't return BitmapSuccess then
146 * its arguments won't have been touched.  This routine should look as much
147 * like the Xlib routine XReadBitmapFile as possible.
148 */
149int
150XmuReadBitmapData(FILE *fstream, unsigned int *width, unsigned int *height,
151		  unsigned char **datap, int *x_hot, int *y_hot)
152{
153    unsigned char *data = NULL;		/* working variable */
154    char line[MAX_SIZE];		/* input line from file */
155    int size;				/* number of bytes of data */
156    char name_and_type[MAX_SIZE];	/* an input line */
157    char *type;				/* for parsing */
158    int value;				/* from an input line */
159    int version10p;			/* boolean, old format */
160    int padding;			/* to handle alignment */
161    int bytes_per_line;			/* per scanline of data */
162    unsigned int ww = 0;		/* width */
163    unsigned int hh = 0;		/* height */
164    int hx = -1;			/* x hotspot */
165    int hy = -1;			/* y hotspot */
166
167#undef  Xmalloc /* see MALLOC_0_RETURNS_NULL in Xlibint.h */
168#define Xmalloc(size) malloc(size)
169
170    /* first time initialization */
171    if (initialized == False) initHexTable();
172
173    /* error cleanup and return macro	*/
174#define	RETURN(code) { if (data) free (data); return code; }
175
176    while (fgets(line, MAX_SIZE, fstream)) {
177	if (strlen(line) == MAX_SIZE-1) {
178	    RETURN (BitmapFileInvalid);
179	}
180	if (sscanf(line,"#define %s %d",name_and_type,&value) == 2) {
181	    if (!(type = strrchr(name_and_type, '_')))
182	      type = name_and_type;
183	    else
184	      type++;
185
186	    if (!strcmp("width", type))
187	      ww = (unsigned int) value;
188	    if (!strcmp("height", type))
189	      hh = (unsigned int) value;
190	    if (!strcmp("hot", type)) {
191		if (type-- == name_and_type || type-- == name_and_type)
192		  continue;
193		if (!strcmp("x_hot", type))
194		  hx = value;
195		if (!strcmp("y_hot", type))
196		  hy = value;
197	    }
198	    continue;
199	}
200
201	if (sscanf(line, "static short %s = {", name_and_type) == 1)
202	  version10p = 1;
203	else if (sscanf(line,"static unsigned char %s = {",name_and_type) == 1)
204	  version10p = 0;
205	else if (sscanf(line, "static char %s = {", name_and_type) == 1)
206	  version10p = 0;
207	else
208	  continue;
209
210	if (!(type = strrchr(name_and_type, '_')))
211	  type = name_and_type;
212	else
213	  type++;
214
215	if (strcmp("bits[]", type))
216	  continue;
217
218	if (!ww || !hh)
219	  RETURN (BitmapFileInvalid);
220
221	if ((ww % 16) && ((ww % 16) < 9) && version10p)
222	  padding = 1;
223	else
224	  padding = 0;
225
226	bytes_per_line = (ww+7)/8 + padding;
227
228	size = bytes_per_line * hh;
229	data = Xmalloc ((unsigned int) size);
230	if (!data)
231	  RETURN (BitmapNoMemory);
232
233	if (version10p) {
234	    unsigned char *ptr;
235	    int bytes;
236
237	    for (bytes=0, ptr=data; bytes<size; (bytes += 2)) {
238		if ((value = NextInt(fstream)) < 0)
239		  RETURN (BitmapFileInvalid);
240		*(ptr++) = value;
241		if (!padding || ((bytes+2) % bytes_per_line))
242		  *(ptr++) = value >> 8;
243	    }
244	} else {
245	    unsigned char *ptr;
246	    int bytes;
247
248	    for (bytes=0, ptr=data; bytes<size; bytes++, ptr++) {
249		if ((value = NextInt(fstream)) < 0)
250		  RETURN (BitmapFileInvalid);
251		*ptr=value;
252	    }
253	}
254	break;
255    }					/* end while */
256
257    if (data == NULL) {
258	RETURN (BitmapFileInvalid);
259    }
260
261    *datap = data;
262    data = NULL;
263    *width = ww;
264    *height = hh;
265    if (x_hot) *x_hot = hx;
266    if (y_hot) *y_hot = hy;
267
268    RETURN (BitmapSuccess);
269}
270
271#if defined(WIN32)
272static int
273access_file(char *path, char *pathbuf, int len_pathbuf, char **pathret)
274{
275    if (access (path, F_OK) == 0) {
276	if (strlen (path) < len_pathbuf)
277	    *pathret = pathbuf;
278	else
279	    *pathret = malloc (strlen (path) + 1);
280	if (*pathret) {
281	    strcpy (*pathret, path);
282	    return 1;
283	}
284    }
285    return 0;
286}
287
288static int
289AccessFile(char *path, char *pathbuf, int len_pathbuf, char **pathret)
290{
291#ifndef MAX_PATH
292#define MAX_PATH 512
293#endif
294
295    unsigned long drives;
296    int i, len;
297    char* drive;
298    char buf[MAX_PATH];
299    char* bufp;
300
301    /* just try the "raw" name first and see if it works */
302    if (access_file (path, pathbuf, len_pathbuf, pathret))
303	return 1;
304
305    /* try the places set in the environment */
306    drive = getenv ("_XBASEDRIVE");
307    if (!drive)
308	drive = "C:";
309    len = strlen (drive) + strlen (path);
310    if (len < MAX_PATH) bufp = buf;
311    else bufp = malloc (len + 1);
312    strcpy (bufp, drive);
313    strcat (bufp, path);
314    if (access_file (bufp, pathbuf, len_pathbuf, pathret)) {
315	if (bufp != buf) free (bufp);
316	return 1;
317    }
318
319    /* one last place to look */
320    drive = getenv ("HOMEDRIVE");
321    if (drive) {
322	len = strlen (drive) + strlen (path);
323	if (len < MAX_PATH) bufp = buf;
324	else bufp = malloc (len + 1);
325	strcpy (bufp, drive);
326	strcat (bufp, path);
327	if (access_file (bufp, pathbuf, len_pathbuf, pathret)) {
328	    if (bufp != buf) free (bufp);
329	    return 1;
330	}
331    }
332
333    /* does OS/2 (with or with gcc-emx) have getdrives? */
334    /* tried everywhere else, go fishing */
335#define C_DRIVE ('C' - 'A')
336#define Z_DRIVE ('Z' - 'A')
337    drives = _getdrives ();
338    for (i = C_DRIVE; i <= Z_DRIVE; i++) { /* don't check on A: or B: */
339	if ((1 << i) & drives) {
340	    len = 2 + strlen (path);
341	    if (len < MAX_PATH) bufp = buf;
342	    else bufp = malloc (len + 1);
343	    *bufp = 'A' + i;
344	    *(bufp + 1) = ':';
345	    *(bufp + 2) = '\0';
346	    strcat (bufp, path);
347	    if (access_file (bufp, pathbuf, len_pathbuf, pathret)) {
348		if (bufp != buf) free (bufp);
349		return 1;
350	    }
351	}
352    }
353    return 0;
354}
355
356FILE *
357fopen_file(char *path, char *mode)
358{
359    char buf[MAX_PATH];
360    char* bufp;
361    void* ret = NULL;
362    UINT olderror = SetErrorMode (SEM_FAILCRITICALERRORS);
363
364    if (AccessFile (path, buf, MAX_PATH, &bufp))
365	ret = fopen (bufp, mode);
366
367    (void) SetErrorMode (olderror);
368
369    if (bufp != buf) free (bufp);
370
371    return ret;
372}
373
374#else
375#define fopen_file fopen
376#endif
377
378
379int
380XmuReadBitmapDataFromFile(_Xconst char *filename, unsigned int *width,
381			       unsigned int *height, unsigned char **datap,
382			       int *x_hot, int *y_hot)
383{
384    FILE *fstream;
385    int status;
386
387    if ((fstream = fopen_file (filename, "r")) == NULL) {
388	return BitmapOpenFailed;
389    }
390    status = XmuReadBitmapData(fstream, width, height, datap, x_hot, y_hot);
391    fclose (fstream);
392    return status;
393}
394