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