1 /* $TOG: IconFile.c /main/13 1997/07/18 17:14:37 samborn $ */
2 /*
3  * Motif
4  *
5  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
6  *
7  * These libraries and programs are free software; you can
8  * redistribute them and/or modify them under the terms of the GNU
9  * Lesser General Public License as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  *
13  * These libraries and programs are distributed in the hope that
14  * they will be useful, but WITHOUT ANY WARRANTY; without even the
15  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16  * PURPOSE. See the GNU Lesser General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with these librararies and programs; if not, write
21  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
22  * Floor, Boston, MA 02110-1301 USA
23  *
24  */
25 /*
26  * HISTORY
27  */
28 /* This module references:
29      XmeGetHomeDirName, _XmOSFindPathParts, _XmOSAbsolutePathName (new in Xmos)
30      _XmOSInitPath (new version that uses absolutepath)
31      _XmInImageCache (in ImageCache)
32      XmeGetIconControlInfo (in ColorObj)
33      _XmHash API
34    and it exports:
35      XmeFlushIconFileCache  (used by CDE)
36      XmGetIconFileName      (used by ImageCache)
37    It is still Dt centric for the PATH variables and the local variables.
38 */
39 
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43 
44 
45 #include <stdio.h>
46 #include <X11/Xlocale.h>
47 
48 #define X_INCLUDE_DIRENT_H
49 #define XOS_USE_XT_LOCKING
50 
51 #ifndef NEED_XOS_R_H
52 #include <X11/Xos_r.h> /* Must precede XmI.h to avoid possible redefinitions
53 			  of MIN() and MAX(). Xos_r.h includes Xos.h */
54 #else
55 #include <Xm/Xmos_r.h>
56 #endif
57 
58 #include "XmosI.h"
59 
60 #include <Xm/IconFileP.h>
61 #include <Xm/ColorObjP.h>
62 #include "XmI.h"
63 #include "HashI.h"
64 
65 #include "ImageCachI.h"
66 
67 #define FIX_1427
68 
69 /**************** vendor dependant defaults ********/
70 /* All this stuff (cached dir) should be moved and possibly merged
71    in Xmos.c, where it belongs */
72 
73 
74 #ifndef X_NOT_STDC_ENV
75 #include <stdlib.h>
76 #include <unistd.h>
77 #endif
78 
79 #include <sys/types.h>
80 
81 #include <fcntl.h>
82 #include <sys/stat.h>
83 
84 
85 #ifdef USE_GETWD
86 #include <sys/param.h>
87 #define MAX_DIR_PATH_LEN    MAXPATHLEN
88 #define getcwd(buf, len)   ((char *) getwd(buf))
89 #else
90 #define MAX_DIR_PATH_LEN    1024
91 #endif
92 #define MAX_USER_NAME_LEN   256
93 
94 #ifndef S_ISDIR
95 #define S_ISDIR(m) ((m & S_IFMT)==S_IFDIR)
96 #endif
97 
98 /**************** end of vendor dependant defaults ********/
99 
100 
101 
102 
103 /**************** Icon PATH defines ********/
104 
105 
106 static XmConst char ABSOLUTE_IPATH[] = "%H%B";
107 static XmConst char ABSOLUTE_PATH[] = "\
108 %P\
109 %S";
110 
111 
112 /******------------------------------------------********/
113 
114 typedef union _DtCachedDirStruct *DtCachedDir;
115 
116 typedef struct _DtCommonCachedDirStruct{
117     int			cachedDirType;
118     int			dirNameLen;
119     String		dirName;
120 }DtCommonCachedDirStruct, *DtCommonCachedDir;
121 
122 typedef struct _DtValidCachedDirStruct{
123     int			cachedDirType;
124     int			dirNameLen;
125     String		dirName;
126     int			numFiles;
127     /*
128      * we allocate both the offsets array and the names themselves in
129      * a heap hanging off the end of this struct
130      */
131     unsigned short	nameOffsets[1];
132     /*
133     String		names
134     */
135 }DtValidCachedDirStruct, *DtValidCachedDir;
136 
137 #define DtVALID_CACHED_DIR	0
138 #define DtINVALID_CACHED_DIR	1
139 #define DtUNCACHED_DIR		2
140 
141 #define MAX_CACHE_DIR_SIZE	(1L << 16)
142 
143 typedef union _DtCachedDirStruct{
144     DtCommonCachedDirStruct	common;
145     DtValidCachedDirStruct 	valid_dir;
146 }DtCachedDirStruct;
147 
148 typedef struct _DtCachedDirListStruct{
149     int			numDirs;
150     int			maxDirs;
151     DtCachedDir	*dirs;
152 }DtCachedDirListStruct;
153 
154 
155 /********    Static Function Declarations    ********/
156 
157 static DtCachedDir MakeCachedDirEntry(
158                         String dirName) ;
159 static int CheckDirCache(
160                         String path);
161 static Boolean TestIconFile(
162                         String path) ;
163 static Boolean CompareIconNames (XmHashKey key_1, XmHashKey key_2);
164 static XmHashValue HashIconName (XmHashKey key);
165 
166 /********    End Static Function Declarations    ********/
167 
168 
169 
170 
171 
172 
173 static DtCachedDir
MakeCachedDirEntry(String dirName)174 MakeCachedDirEntry(String dirName)
175 {
176     DIR * 		fileDesc = NULL;
177     struct dirent	*currDirect;
178     DtCachedDir 	cachedDir = NULL;
179     int			cachedDirType;
180 
181     if ((fileDesc = opendir (dirName)) == NULL)  {
182 	/* invalid directory */
183 	cachedDirType = DtINVALID_CACHED_DIR;
184     }
185     else {
186 	int	bufLen, oldBufLen = 0;
187 	char	stackBuf[MAX_CACHE_DIR_SIZE];
188 	char    *p;
189 	int	numFiles = 0;
190 	int	nameHeapSize = 0;
191 	_Xreaddirparams dirEntryBuf;
192 
193         /*
194          * Original code was caching each struct direct in stackBuf.
195          * Instead, just cache currDirect->d_name, null-terminated.
196          */
197         cachedDirType = DtVALID_CACHED_DIR;
198 	while ((currDirect = _XReaddir(fileDesc, dirEntryBuf)) != NULL) {
199 	  bufLen = strlen(currDirect->d_name);
200 	  if (bufLen + oldBufLen + 1 >= MAX_CACHE_DIR_SIZE) {
201 	    /*
202 	     * don't cache this one
203 	     */
204 	    cachedDirType = DtUNCACHED_DIR;
205 	    break;
206 	  } else {
207 	    (void) memcpy(&(stackBuf[oldBufLen]), currDirect->d_name, bufLen);
208 	    oldBufLen += bufLen;
209             stackBuf[oldBufLen++] = '\0';
210 	  }
211 	}
212 	if (oldBufLen == 0) {
213 	  /* invalid entry */
214 	  cachedDirType = DtINVALID_CACHED_DIR;
215 	}
216 
217         if( cachedDirType == DtVALID_CACHED_DIR)
218         {
219 	    DtValidCachedDir	validDir;
220 	    String		nameHeap;
221 	    Cardinal		i;
222 
223             /*
224              * Go through stackBuf and count the length of all
225              * the names.  Don't count the nulls.
226              */
227             for (p = stackBuf ; p - stackBuf < oldBufLen;
228 		 p = p + strlen(p) + 1) {
229 
230 		numFiles++;
231 		nameHeapSize += strlen(p);
232 	    }
233 	    /*
234 	     * we allocate an extra nameOffset to track the length of
235 	     * the last name
236 	     */
237 	    validDir = (DtValidCachedDir)
238 	      XtMalloc((sizeof(DtValidCachedDirStruct)) +
239 		       (sizeof(validDir->nameOffsets[0]) * numFiles) +
240 		       (nameHeapSize));
241 
242 	    validDir->dirNameLen = strlen(dirName);
243 	    validDir->dirName = dirName;
244 	    validDir->numFiles = numFiles;
245 	    cachedDirType =
246 	      validDir->cachedDirType =
247 		DtVALID_CACHED_DIR;
248 	    validDir->nameOffsets[0] = 0;
249 	    nameHeap = (String)
250 	      &(validDir->nameOffsets[numFiles + 1]);
251 
252             /* Copy the strings from stackBuf to nameHeap.  Omit the nulls. */
253             for (i = 0, p = stackBuf; i < validDir->numFiles;
254                  i++, p = p + strlen(p) + 1) {
255 
256 		validDir->nameOffsets[i + 1] =
257 		  validDir->nameOffsets[i] + strlen(p);
258 		memcpy(&(nameHeap[validDir->nameOffsets[i]]), p, strlen(p));
259 	    }
260           cachedDir = (DtCachedDir)validDir ;
261 	}
262     }
263     switch (cachedDirType) {
264       case DtINVALID_CACHED_DIR:
265       case DtUNCACHED_DIR:
266 	cachedDir = (DtCachedDir)
267 	  XtMalloc(sizeof(DtCommonCachedDirStruct));
268 	cachedDir->common.cachedDirType =
269 	  cachedDirType;
270 	cachedDir->common.dirNameLen = strlen(dirName);
271 	cachedDir->common.dirName = dirName;
272 	break;
273       case DtVALID_CACHED_DIR:
274 	break;
275     }
276     if (fileDesc != NULL)
277       closedir(fileDesc);
278     return cachedDir;
279 }
280 
281 static 	DtCachedDirListStruct cacheList;
282 
283 
284 
285 void
XmeFlushIconFileCache(String path)286 XmeFlushIconFileCache(String	path)
287 {
288     Cardinal	dirNameLen;
289     Cardinal 	i;
290 
291     _XmProcessLock();
292 
293     /*
294      * loop thru the dir list. if no path was specified then flush the
295      * entire cache.
296      */
297     if (path)
298       dirNameLen = strlen(path);
299     else
300       dirNameLen = 0;
301     for (i = 0; i < cacheList.numDirs; i++) {
302 	DtValidCachedDir	currDir;
303 
304 	currDir = (DtValidCachedDir)cacheList.dirs[i];
305 	if (!path ||
306 	    ((currDir->dirNameLen == dirNameLen) &&
307 	     (strncmp(currDir->dirName, path, dirNameLen) == 0))) {
308 	    XtFree(currDir->dirName);
309 	    XtFree((char *)currDir);
310 
311 	    if (path) {
312 		/* ripple down the dir array */
313 		for (; i < cacheList.numDirs - 1; i++)
314 		  cacheList.dirs[i] = cacheList.dirs[i+1];
315 		cacheList.numDirs--;
316 		_XmProcessUnlock();
317 		return;
318 	    }
319 	}
320     }
321     if (path && (i == cacheList.numDirs)) {
322 	_XmProcessUnlock();
323 	return;
324     }
325     cacheList.numDirs = 0;
326     /* don't free the dirList itself */
327     _XmProcessUnlock();
328 }
329 
330 
331 #ifndef XTHREADS
332 static String GdirName;
333 static String GleafName;
334 #endif
335 
336 static int
CheckDirCache(String path)337 CheckDirCache(String	path)
338 
339 {
340     String	dirName;
341     String	filePtr;
342     String	suffixPtr;
343     int		numDirs, dirNameLen, fileNameLen;
344     Cardinal	i, j;
345     char   	stackString[MAX_DIR_PATH_LEN];
346 
347     (void) _XmOSAbsolutePathName(path, &path, stackString);
348     _XmOSFindPathParts(path, &filePtr, &suffixPtr);
349 
350     if (path == filePtr) {
351 	dirNameLen = 0;
352 	fileNameLen = strlen(path);
353     }
354     else {
355 	/* take the slash into account */
356 	dirNameLen = filePtr - path - 1;
357 	fileNameLen = strlen(path) - dirNameLen - 1;
358     }
359 
360     /*
361      * set global variable for later use
362      */
363 #ifndef XTHREADS
364     GleafName = filePtr;
365 #endif
366 
367     if (dirNameLen == 0) {
368 	return DtINVALID_CACHED_DIR;
369     }
370 
371     /*
372      * loop thru the dir list. on the last pass create the new cached
373      * dir and process it.
374      */
375     _XmProcessLock();
376 
377     numDirs = cacheList.numDirs;
378     for (i = 0; i <= numDirs; i++) {
379 	String			currName;
380 	int			currNameLen;
381 	String			nameHeap;
382 	DtValidCachedDir	currDir;
383 
384 	if (i == cacheList.numDirs) {
385 
386 	    /*
387 	     * we didn't get a hit on the directory list so create a new one
388 	     */
389 	    if (cacheList.numDirs == cacheList.maxDirs) {
390 		cacheList.maxDirs += 16;
391 		cacheList.dirs = (DtCachedDir *)
392 		  XtRealloc((char *)cacheList.dirs,
393 			    cacheList.maxDirs * sizeof (DtCachedDir));
394 	    }
395 	    dirName = strncpy(XtMalloc(dirNameLen+1), path, dirNameLen);
396 	    dirName[dirNameLen] = '\0';
397 	    cacheList.dirs[cacheList.numDirs++] = MakeCachedDirEntry(dirName);
398 	}
399 	currDir = (DtValidCachedDir)cacheList.dirs[i];
400 
401 	/*
402 	 * set global variable
403 	 */
404 #ifndef XTHREADS
405 	GdirName = currDir->dirName;
406 #endif
407 
408 	if ((currDir->dirNameLen == dirNameLen) &&
409 	    (strncmp(currDir->dirName, path, dirNameLen) == 0)) {
410 
411 	    switch(currDir->cachedDirType) {
412 	      case DtINVALID_CACHED_DIR:
413 	      case DtUNCACHED_DIR:
414 		_XmProcessUnlock();
415 		return currDir->cachedDirType;
416 		break;
417 	      case DtVALID_CACHED_DIR:
418 		nameHeap = (String)
419 		  &(currDir->nameOffsets[currDir->numFiles + 1]);
420 		for (j = 0; j < currDir->numFiles; j++) {
421 		    /*
422 		     * nameOffsets has an extra offset to indicate the
423 		     * end of the last name (to handle border condition
424 		     */
425 		    currNameLen = (currDir->nameOffsets[j + 1] -
426 				   currDir->nameOffsets[j]);
427 		    if (currNameLen == fileNameLen) {
428 			currName =  &(nameHeap[currDir->nameOffsets[j]]);
429 			if (strncmp(currName, filePtr, currNameLen) == 0) {
430 			    _XmProcessUnlock();
431 			    return DtVALID_CACHED_DIR;
432 			}
433 		    }
434 		}
435 		_XmProcessUnlock();
436 		return DtINVALID_CACHED_DIR;
437 	    }
438 	}
439     }
440     _XmProcessUnlock();
441     return DtINVALID_CACHED_DIR;
442 }
443 
444 static String
find_slash(String str)445 find_slash(String str)
446 {
447   int n;
448   if (MB_CUR_MAX == 1) {
449       return strchr(str, '/');
450   } else {
451 #ifndef NO_MULTIBYTE
452       while ((n = mblen(str, MB_CUR_MAX)) >0) {
453 #else
454       if (!str) return NULL;
455       while ((n = *str ? 1 : 0) > 0) {
456 #endif
457 #ifndef NO_MULTIBYTE
458         if (n == 1 && *str == '/')
459             return str;
460         str += n;
461 #else
462 	if (*str == '/')
463 	    return str;
464 	str++;
465 #endif
466       }
467       return NULL;
468   }
469 }
470 
471 static Boolean
472 TestIconFile(String	path)
473 {
474     struct stat status;
475     int	dirCacheType;
476 
477     if (!path || !*path)
478       return False;
479 
480     /* if there is no directory information in the name, it's
481        a local file, check here or CheckDirCache will fail */
482 
483     if (!find_slash(path)) {
484 	dirCacheType = DtUNCACHED_DIR ;
485 #ifndef XTHREADS
486 	GleafName = path ;
487 	GdirName = "." ;
488 #endif
489     } else
490 	dirCacheType = CheckDirCache(path);
491 
492     switch(dirCacheType) {
493       case DtVALID_CACHED_DIR:
494 	return True;
495 
496       case DtINVALID_CACHED_DIR:
497 	return False;
498 
499       case DtUNCACHED_DIR:
500 	return (access(path, R_OK) == 0 &&	/* exists and is readable */
501 		stat(path, &status) == 0 &&	/* get the status */
502 		S_ISDIR(status.st_mode) == 0	/* not a directory */
503 		);
504     }
505 
506     return False ;
507 }
508 
509 
510 /*********** Hash table stuff  */
511 
512 
513 typedef struct _DtIconNameEntryRec{
514     String	dirName;
515     String	leafName;
516     String	key_name;
517 }DtIconNameEntryRec, *DtIconNameEntry;
518 
519 
520 /* Compare two icon names from icon entry rec */
521 static Boolean
522 CompareIconNames (XmHashKey key_1,
523 		XmHashKey key_2)
524 {
525   DtIconNameEntry data_1 = (DtIconNameEntry) key_1;
526   DtIconNameEntry data_2 = (DtIconNameEntry) key_2;
527 
528   return ((data_1->key_name == data_2->key_name) ||
529 	  (strcmp(data_1->key_name, data_2->key_name) == 0));
530 }
531 
532 
533 /* Hash an icon name . */
534 static XmHashValue
535 HashIconName (XmHashKey key)
536 {
537   DtIconNameEntry data = (DtIconNameEntry) key;
538   unsigned int len = strlen(data->key_name);
539 
540   return (((len << 8) | data->key_name[0]) << 8) | data->key_name[len];
541 }
542 
543 
544 String
545 XmGetIconFileName(
546     Screen	*screen,
547     String	imageInstanceName,
548     String	imageClassName,
549     String	hostPrefix,
550     unsigned int size)
551 {
552     Display		*display = DisplayOfScreen(screen);
553     String		fileName = NULL;
554     String		names[2];
555     String		names_w_size[2];
556     XmConst char       *bPath, *iPath;
557     Cardinal		i;
558     Boolean		useColor;
559     Boolean		useMask;
560     Boolean		useIconFileCache;
561     Boolean		absolute = 0;
562     XtFilePredicate	testFileFunc;
563     String		homedir = NULL ;
564     static String	iconPath = NULL;
565     static String	bmPath = NULL;
566     static XmHashTable iconNameCache = NULL;
567     char 		stackString[MAX_DIR_PATH_LEN];
568 
569 #define B_SUB	0
570 #define P_SUB	1
571 #define M_SUB	2
572 #define H_SUB	3
573 
574     SubstitutionRec iconSubs[] = {
575 	{'B', NULL},	/* bitmap name */
576 	{'P', NULL},	/* alternate bitmap name BC */
577 	{'M', NULL},	/* magnitude */
578 	{'H', NULL},	/* host prefix */
579     };
580 
581     XtAppContext app;
582 
583     app = XtDisplayToApplicationContext(display);
584 
585     _XmAppLock(app);
586 
587     /* start by asking some screen state */
588     (void)XmeGetIconControlInfo(screen,
589 				&useMask,  /* not used here */
590 				&useColor,
591 				&useIconFileCache);
592 
593     _XmProcessLock();
594 
595     /* generate the icon paths once per application: iconPath and bmPath */
596     if (!iconNameCache) {
597 	Boolean		junkBoolean;
598 
599 	iconNameCache =  _XmAllocHashTable(100,
600 					   CompareIconNames, HashIconName);
601 
602 	cacheList.numDirs =
603 	  cacheList.maxDirs = 0;
604 	cacheList.dirs = NULL;
605 
606 	homedir = XmeGetHomeDirName();
607 	strcpy(stackString, homedir) ;
608 
609 	if (useColor) {
610 	    iconPath = _XmOSInitPath(NULL, "XMICONSEARCHPATH", &junkBoolean);
611 	}
612 	else {
613 	    iconPath = _XmOSInitPath(NULL, "XMICONBMSEARCHPATH", &junkBoolean);
614 	}
615 
616 	/* 1.2 path as a fallback */
617 	bmPath = _XmOSInitPath(NULL, "XBMLANGPATH", &junkBoolean);
618 
619     }
620 
621     switch (size) {
622       case XmTINY_ICON_SIZE:
623 	iconSubs[M_SUB].substitution = ".t";
624 	break;
625       case XmSMALL_ICON_SIZE:
626 	iconSubs[M_SUB].substitution = ".s";
627 	break;
628       case XmMEDIUM_ICON_SIZE:
629 	iconSubs[M_SUB].substitution = ".m";
630 	break;
631       case XmLARGE_ICON_SIZE:
632 	iconSubs[M_SUB].substitution = ".l";
633 	break;
634       case XmUNSPECIFIED_ICON_SIZE:
635 	iconSubs[M_SUB].substitution = NULL;
636 	break;
637     }
638 
639     iconSubs[H_SUB].substitution = hostPrefix;
640 
641     if (useIconFileCache)
642       testFileFunc = TestIconFile;
643     else
644       testFileFunc = NULL;
645 
646     names[0] 	    = imageInstanceName;
647     names[1] 	    = imageClassName;
648     names_w_size[0] = names_w_size[1] = (String)NULL;
649 
650     /** loop over the two names */
651     for (i = 0; i < 2; i++) {
652 
653 	if (names[i] == NULL)
654 	  continue;
655 
656 	if ((absolute = _XmOSAbsolutePathName(names[i], &names[i],
657 					      stackString)) != FALSE) {
658 	    iPath = ABSOLUTE_IPATH;
659 	    bPath = ABSOLUTE_PATH;
660 	}
661 	else {
662 	    iPath = iconPath;
663 	    bPath = bmPath;
664 	}
665 
666 	iconSubs[B_SUB].substitution = names[i];
667 	iconSubs[P_SUB].substitution = names[i];
668 
669        /* need to add size suffix if size is specified */
670         if (size != XmUNSPECIFIED_ICON_SIZE) {
671            int basenameLen = strlen(names[i]);
672            int sizeLen = strlen(iconSubs[M_SUB].substitution);
673 	   char * ext_name = XtMalloc(basenameLen + sizeLen + 1);
674 	   /* XmosP.h takes care of bcopy translation */
675 	   memmove(&ext_name[0], names[i], basenameLen);
676 	   memmove(&ext_name[basenameLen],
677 	           iconSubs[M_SUB].substitution, sizeLen);
678 	   ext_name[basenameLen + sizeLen] = '\0';
679 
680            names_w_size[i] = ext_name;
681 
682         } else
683            names_w_size[i] = NULL;
684 
685        /*
686         * try to see if its already in the image cache
687 	*/
688 	if (_XmInImageCache(names[i]))
689 	  fileName = XtNewString(names[i]);
690 
691 
692 	/*
693 	 * optimization to check all expansions in cache
694 	 */
695 	if (!fileName) {
696 	    DtIconNameEntry iNameEntry;
697 	    DtIconNameEntryRec  iNameData ;
698 
699 	    iNameData.key_name = (names_w_size[i])?names_w_size[i]:names[i];
700 
701 	    iNameEntry =  (DtIconNameEntry)
702 		_XmGetHashEntry(iconNameCache, (XmHashKey)&iNameData);
703 
704 	    if (iNameEntry) {
705 		int dirLen, leafLen;
706 
707 		dirLen = strlen(iNameEntry->dirName);
708 		leafLen = strlen(iNameEntry->leafName);
709 		fileName = XtMalloc(dirLen + leafLen + 2);
710 
711 		memmove(&fileName[0],
712 	      		iNameEntry->dirName,
713 			dirLen);
714 #ifdef FIX_1427
715 		if (dirLen == 0) {
716 			memmove(&fileName[dirLen], iNameEntry->leafName, leafLen);
717 			fileName[dirLen + leafLen] = '\0';
718 		} else {
719 #endif
720 		fileName[dirLen] = '/';
721 		memmove(&fileName[dirLen + 1],
722 	      		iNameEntry->leafName,
723 			leafLen);
724 
725 		fileName[dirLen + leafLen + 1] = '\0';
726 #ifdef FIX_1427
727 		}
728 #endif
729 	    }
730 	}
731 
732 	if (fileName) {
733 	  /*
734 	   * CDExc20823 (memory leak): free names_w_size[i]
735 	   * if it is not NULL.
736 	   * NOTE: This code could be reorganized to do
737 	   *   _XmInImageCache() at the top of this loop
738 	   *   so we could avoid unnecessary malloc's for
739 	   *   names_w_size, but I wanted to minimize the
740 	   *   code impact of this defect for now.
741 	   */
742 	  for (i = 0; i < 2; i++)
743 	  {
744 	    if (names_w_size[i] != (String)NULL)
745 	      XtFree(names_w_size[i]);
746 	  }
747 
748 	  _XmProcessUnlock();
749 	  _XmAppUnlock(app);
750 	  return fileName;
751         }
752 
753 	/*******************************
754 	 * first try XPM and then XBM
755 	 ******************************/
756 	fileName =
757 	  XtResolvePathname(display, "icons", NULL,
758 			    NULL, iPath, iconSubs,
759 			    XtNumber(iconSubs),
760 			    (XtFilePredicate) testFileFunc);
761 
762 	if (fileName == NULL) {
763 	    fileName =
764 	      XtResolvePathname(display, "bitmaps", NULL,
765 				NULL, bPath, iconSubs,
766 				XtNumber(iconSubs),
767 				(XtFilePredicate) testFileFunc);
768 	}
769 
770 	if (fileName)
771 	  break;
772     }
773     _XmProcessUnlock();
774 
775     if (fileName && !absolute) {
776 	/* register it in name cache */
777 	DtIconNameEntry 	iNameEntry;
778 	String name_used = (names_w_size[i])? names_w_size[i] : names[i] ;
779 
780 	/** alloc a icon cache entry **/
781 	iNameEntry = (DtIconNameEntry) XtMalloc(sizeof(DtIconNameEntryRec));
782 	iNameEntry->key_name = XtNewString(name_used);
783 
784 #ifndef XTHREADS
785         if (useIconFileCache)
786         {
787 	   iNameEntry->dirName = XtNewString(GdirName);
788 	   iNameEntry->leafName = XtNewString(GleafName);
789         }
790         else
791 #endif
792         {
793            String	dirName;
794            String	filePtr;
795            String 	suffixPtr;
796            int	dirNameLen;
797 
798            _XmOSFindPathParts(fileName, &filePtr, &suffixPtr);
799 
800            if (fileName == filePtr)
801 	       dirNameLen = 0;
802            else {
803 	       /* take the slash into account */
804 	       dirNameLen = filePtr - fileName - 1;
805            }
806 
807            dirName = (String)XtMalloc(dirNameLen + 1);
808 	   strncpy(dirName, fileName, dirNameLen);
809 	   dirName[dirNameLen] = '\0';
810 
811 	   iNameEntry->dirName = dirName;
812 	   iNameEntry->leafName = XtNewString(filePtr);
813         }
814 
815 	_XmProcessLock();
816         _XmAddHashEntry(iconNameCache, (XmHashKey)iNameEntry,
817 			(XtPointer)iNameEntry);
818 	_XmProcessUnlock();
819     }
820 
821     /*
822      * CDExc20823 (memory leak): free names_w_size[i] if not NULL.
823      */
824     for (i = 0; i < 2; i++)
825     {
826 	if (names_w_size[i] != (String)NULL)
827 	    XtFree(names_w_size[i]);
828     }
829 
830     _XmAppUnlock(app);
831     return fileName;
832 }
833 
834 
835 
836 
837 
838 
839 
840