/* $TOG: IconFile.c /main/13 1997/07/18 17:14:37 samborn $ */
/*
* Motif
*
* Copyright (c) 1987-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*
*/
/*
* HISTORY
*/
/* This module references:
XmeGetHomeDirName, _XmOSFindPathParts, _XmOSAbsolutePathName (new in Xmos)
_XmOSInitPath (new version that uses absolutepath)
_XmInImageCache (in ImageCache)
XmeGetIconControlInfo (in ColorObj)
_XmHash API
and it exports:
XmeFlushIconFileCache (used by CDE)
XmGetIconFileName (used by ImageCache)
It is still Dt centric for the PATH variables and the local variables.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <X11/Xlocale.h>
#define X_INCLUDE_DIRENT_H
#define XOS_USE_XT_LOCKING
#ifndef NEED_XOS_R_H
#include <X11/Xos_r.h> /* Must precede XmI.h to avoid possible redefinitions
of MIN() and MAX(). Xos_r.h includes Xos.h */
#else
#include <Xm/Xmos_r.h>
#endif
#include "XmosI.h"
#include <Xm/IconFileP.h>
#include <Xm/ColorObjP.h>
#include "XmI.h"
#include "HashI.h"
#include "ImageCachI.h"
#define FIX_1427
/**************** vendor dependant defaults ********/
/* All this stuff (cached dir) should be moved and possibly merged
in Xmos.c, where it belongs */
#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#include <unistd.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef USE_GETWD
#include <sys/param.h>
#define MAX_DIR_PATH_LEN MAXPATHLEN
#define getcwd(buf, len) ((char *) getwd(buf))
#else
#define MAX_DIR_PATH_LEN 1024
#endif
#define MAX_USER_NAME_LEN 256
#ifndef S_ISDIR
#define S_ISDIR(m) ((m & S_IFMT)==S_IFDIR)
#endif
/**************** end of vendor dependant defaults ********/
/**************** Icon PATH defines ********/
static XmConst char ABSOLUTE_IPATH[] = "%H%B";
static XmConst char ABSOLUTE_PATH[] = "\
%P\
%S";
/******------------------------------------------********/
typedef union _DtCachedDirStruct *DtCachedDir;
typedef struct _DtCommonCachedDirStruct{
int cachedDirType;
int dirNameLen;
String dirName;
}DtCommonCachedDirStruct, *DtCommonCachedDir;
typedef struct _DtValidCachedDirStruct{
int cachedDirType;
int dirNameLen;
String dirName;
int numFiles;
/*
* we allocate both the offsets array and the names themselves in
* a heap hanging off the end of this struct
*/
unsigned short nameOffsets[1];
/*
String names
*/
}DtValidCachedDirStruct, *DtValidCachedDir;
#define DtVALID_CACHED_DIR 0
#define DtINVALID_CACHED_DIR 1
#define DtUNCACHED_DIR 2
#define MAX_CACHE_DIR_SIZE (1L << 16)
typedef union _DtCachedDirStruct{
DtCommonCachedDirStruct common;
DtValidCachedDirStruct valid_dir;
}DtCachedDirStruct;
typedef struct _DtCachedDirListStruct{
int numDirs;
int maxDirs;
DtCachedDir *dirs;
}DtCachedDirListStruct;
/******** Static Function Declarations ********/
static DtCachedDir MakeCachedDirEntry(
String dirName) ;
static int CheckDirCache(
String path);
static Boolean TestIconFile(
String path) ;
static Boolean CompareIconNames (XmHashKey key_1, XmHashKey key_2);
static XmHashValue HashIconName (XmHashKey key);
/******** End Static Function Declarations ********/
static DtCachedDir
MakeCachedDirEntry(String dirName)
{
DIR * fileDesc = NULL;
struct dirent *currDirect;
DtCachedDir cachedDir = NULL;
int cachedDirType;
if ((fileDesc = opendir (dirName)) == NULL) {
/* invalid directory */
cachedDirType = DtINVALID_CACHED_DIR;
}
else {
int bufLen, oldBufLen = 0;
char stackBuf[MAX_CACHE_DIR_SIZE];
char *p;
int numFiles = 0;
int nameHeapSize = 0;
_Xreaddirparams dirEntryBuf;
/*
* Original code was caching each struct direct in stackBuf.
* Instead, just cache currDirect->d_name, null-terminated.
*/
cachedDirType = DtVALID_CACHED_DIR;
while ((currDirect = _XReaddir(fileDesc, dirEntryBuf)) != NULL) {
bufLen = strlen(currDirect->d_name);
if (bufLen + oldBufLen + 1 >= MAX_CACHE_DIR_SIZE) {
/*
* don't cache this one
*/
cachedDirType = DtUNCACHED_DIR;
break;
} else {
(void) memcpy(&(stackBuf[oldBufLen]), currDirect->d_name, bufLen);
oldBufLen += bufLen;
stackBuf[oldBufLen++] = '\0';
}
}
if (oldBufLen == 0) {
/* invalid entry */
cachedDirType = DtINVALID_CACHED_DIR;
}
if( cachedDirType == DtVALID_CACHED_DIR)
{
DtValidCachedDir validDir;
String nameHeap;
Cardinal i;
/*
* Go through stackBuf and count the length of all
* the names. Don't count the nulls.
*/
for (p = stackBuf ; p - stackBuf < oldBufLen;
p = p + strlen(p) + 1) {
numFiles++;
nameHeapSize += strlen(p);
}
/*
* we allocate an extra nameOffset to track the length of
* the last name
*/
validDir = (DtValidCachedDir)
XtMalloc((sizeof(DtValidCachedDirStruct)) +
(sizeof(validDir->nameOffsets[0]) * numFiles) +
(nameHeapSize));
validDir->dirNameLen = strlen(dirName);
validDir->dirName = dirName;
validDir->numFiles = numFiles;
cachedDirType =
validDir->cachedDirType =
DtVALID_CACHED_DIR;
validDir->nameOffsets[0] = 0;
nameHeap = (String)
&(validDir->nameOffsets[numFiles + 1]);
/* Copy the strings from stackBuf to nameHeap. Omit the nulls. */
for (i = 0, p = stackBuf; i < validDir->numFiles;
i++, p = p + strlen(p) + 1) {
validDir->nameOffsets[i + 1] =
validDir->nameOffsets[i] + strlen(p);
memcpy(&(nameHeap[validDir->nameOffsets[i]]), p, strlen(p));
}
cachedDir = (DtCachedDir)validDir ;
}
}
switch (cachedDirType) {
case DtINVALID_CACHED_DIR:
case DtUNCACHED_DIR:
cachedDir = (DtCachedDir)
XtMalloc(sizeof(DtCommonCachedDirStruct));
cachedDir->common.cachedDirType =
cachedDirType;
cachedDir->common.dirNameLen = strlen(dirName);
cachedDir->common.dirName = dirName;
break;
case DtVALID_CACHED_DIR:
break;
}
if (fileDesc != NULL)
closedir(fileDesc);
return cachedDir;
}
static DtCachedDirListStruct cacheList;
void
XmeFlushIconFileCache(String path)
{
Cardinal dirNameLen;
Cardinal i;
_XmProcessLock();
/*
* loop thru the dir list. if no path was specified then flush the
* entire cache.
*/
if (path)
dirNameLen = strlen(path);
else
dirNameLen = 0;
for (i = 0; i < cacheList.numDirs; i++) {
DtValidCachedDir currDir;
currDir = (DtValidCachedDir)cacheList.dirs[i];
if (!path ||
((currDir->dirNameLen == dirNameLen) &&
(strncmp(currDir->dirName, path, dirNameLen) == 0))) {
XtFree(currDir->dirName);
XtFree((char *)currDir);
if (path) {
/* ripple down the dir array */
for (; i < cacheList.numDirs - 1; i++)
cacheList.dirs[i] = cacheList.dirs[i+1];
cacheList.numDirs--;
_XmProcessUnlock();
return;
}
}
}
if (path && (i == cacheList.numDirs)) {
_XmProcessUnlock();
return;
}
cacheList.numDirs = 0;
/* don't free the dirList itself */
_XmProcessUnlock();
}
#ifndef XTHREADS
static String GdirName;
static String GleafName;
#endif
static int
CheckDirCache(String path)
{
String dirName;
String filePtr;
String suffixPtr;
int numDirs, dirNameLen, fileNameLen;
Cardinal i, j;
char stackString[MAX_DIR_PATH_LEN];
(void) _XmOSAbsolutePathName(path, &path, stackString);
_XmOSFindPathParts(path, &filePtr, &suffixPtr);
if (path == filePtr) {
dirNameLen = 0;
fileNameLen = strlen(path);
}
else {
/* take the slash into account */
dirNameLen = filePtr - path - 1;
fileNameLen = strlen(path) - dirNameLen - 1;
}
/*
* set global variable for later use
*/
#ifndef XTHREADS
GleafName = filePtr;
#endif
if (dirNameLen == 0) {
return DtINVALID_CACHED_DIR;
}
/*
* loop thru the dir list. on the last pass create the new cached
* dir and process it.
*/
_XmProcessLock();
numDirs = cacheList.numDirs;
for (i = 0; i <= numDirs; i++) {
String currName;
int currNameLen;
String nameHeap;
DtValidCachedDir currDir;
if (i == cacheList.numDirs) {
/*
* we didn't get a hit on the directory list so create a new one
*/
if (cacheList.numDirs == cacheList.maxDirs) {
cacheList.maxDirs += 16;
cacheList.dirs = (DtCachedDir *)
XtRealloc((char *)cacheList.dirs,
cacheList.maxDirs * sizeof (DtCachedDir));
}
dirName = strncpy(XtMalloc(dirNameLen+1), path, dirNameLen);
dirName[dirNameLen] = '\0';
cacheList.dirs[cacheList.numDirs++] = MakeCachedDirEntry(dirName);
}
currDir = (DtValidCachedDir)cacheList.dirs[i];
/*
* set global variable
*/
#ifndef XTHREADS
GdirName = currDir->dirName;
#endif
if ((currDir->dirNameLen == dirNameLen) &&
(strncmp(currDir->dirName, path, dirNameLen) == 0)) {
switch(currDir->cachedDirType) {
case DtINVALID_CACHED_DIR:
case DtUNCACHED_DIR:
_XmProcessUnlock();
return currDir->cachedDirType;
break;
case DtVALID_CACHED_DIR:
nameHeap = (String)
&(currDir->nameOffsets[currDir->numFiles + 1]);
for (j = 0; j < currDir->numFiles; j++) {
/*
* nameOffsets has an extra offset to indicate the
* end of the last name (to handle border condition
*/
currNameLen = (currDir->nameOffsets[j + 1] -
currDir->nameOffsets[j]);
if (currNameLen == fileNameLen) {
currName = &(nameHeap[currDir->nameOffsets[j]]);
if (strncmp(currName, filePtr, currNameLen) == 0) {
_XmProcessUnlock();
return DtVALID_CACHED_DIR;
}
}
}
_XmProcessUnlock();
return DtINVALID_CACHED_DIR;
}
}
}
_XmProcessUnlock();
return DtINVALID_CACHED_DIR;
}
static String
find_slash(String str)
{
int n;
if (MB_CUR_MAX == 1) {
return strchr(str, '/');
} else {
#ifndef NO_MULTIBYTE
while ((n = mblen(str, MB_CUR_MAX)) >0) {
#else
if (!str) return NULL;
while ((n = *str ? 1 : 0) > 0) {
#endif
#ifndef NO_MULTIBYTE
if (n == 1 && *str == '/')
return str;
str += n;
#else
if (*str == '/')
return str;
str++;
#endif
}
return NULL;
}
}
static Boolean
TestIconFile(String path)
{
struct stat status;
int dirCacheType;
if (!path || !*path)
return False;
/* if there is no directory information in the name, it's
a local file, check here or CheckDirCache will fail */
if (!find_slash(path)) {
dirCacheType = DtUNCACHED_DIR ;
#ifndef XTHREADS
GleafName = path ;
GdirName = "." ;
#endif
} else
dirCacheType = CheckDirCache(path);
switch(dirCacheType) {
case DtVALID_CACHED_DIR:
return True;
case DtINVALID_CACHED_DIR:
return False;
case DtUNCACHED_DIR:
return (access(path, R_OK) == 0 && /* exists and is readable */
stat(path, &status) == 0 && /* get the status */
S_ISDIR(status.st_mode) == 0 /* not a directory */
);
}
return False ;
}
/*********** Hash table stuff */
typedef struct _DtIconNameEntryRec{
String dirName;
String leafName;
String key_name;
}DtIconNameEntryRec, *DtIconNameEntry;
/* Compare two icon names from icon entry rec */
static Boolean
CompareIconNames (XmHashKey key_1,
XmHashKey key_2)
{
DtIconNameEntry data_1 = (DtIconNameEntry) key_1;
DtIconNameEntry data_2 = (DtIconNameEntry) key_2;
return ((data_1->key_name == data_2->key_name) ||
(strcmp(data_1->key_name, data_2->key_name) == 0));
}
/* Hash an icon name . */
static XmHashValue
HashIconName (XmHashKey key)
{
DtIconNameEntry data = (DtIconNameEntry) key;
unsigned int len = strlen(data->key_name);
return (((len << 8) | data->key_name[0]) << 8) | data->key_name[len];
}
String
XmGetIconFileName(
Screen *screen,
String imageInstanceName,
String imageClassName,
String hostPrefix,
unsigned int size)
{
Display *display = DisplayOfScreen(screen);
String fileName = NULL;
String names[2];
String names_w_size[2];
XmConst char *bPath, *iPath;
Cardinal i;
Boolean useColor;
Boolean useMask;
Boolean useIconFileCache;
Boolean absolute = 0;
XtFilePredicate testFileFunc;
String homedir = NULL ;
static String iconPath = NULL;
static String bmPath = NULL;
static XmHashTable iconNameCache = NULL;
char stackString[MAX_DIR_PATH_LEN];
#define B_SUB 0
#define P_SUB 1
#define M_SUB 2
#define H_SUB 3
SubstitutionRec iconSubs[] = {
{'B', NULL}, /* bitmap name */
{'P', NULL}, /* alternate bitmap name BC */
{'M', NULL}, /* magnitude */
{'H', NULL}, /* host prefix */
};
XtAppContext app;
app = XtDisplayToApplicationContext(display);
_XmAppLock(app);
/* start by asking some screen state */
(void)XmeGetIconControlInfo(screen,
&useMask, /* not used here */
&useColor,
&useIconFileCache);
_XmProcessLock();
/* generate the icon paths once per application: iconPath and bmPath */
if (!iconNameCache) {
Boolean junkBoolean;
iconNameCache = _XmAllocHashTable(100,
CompareIconNames, HashIconName);
cacheList.numDirs =
cacheList.maxDirs = 0;
cacheList.dirs = NULL;
homedir = XmeGetHomeDirName();
strcpy(stackString, homedir) ;
if (useColor) {
iconPath = _XmOSInitPath(NULL, "XMICONSEARCHPATH", &junkBoolean);
}
else {
iconPath = _XmOSInitPath(NULL, "XMICONBMSEARCHPATH", &junkBoolean);
}
/* 1.2 path as a fallback */
bmPath = _XmOSInitPath(NULL, "XBMLANGPATH", &junkBoolean);
}
switch (size) {
case XmTINY_ICON_SIZE:
iconSubs[M_SUB].substitution = ".t";
break;
case XmSMALL_ICON_SIZE:
iconSubs[M_SUB].substitution = ".s";
break;
case XmMEDIUM_ICON_SIZE:
iconSubs[M_SUB].substitution = ".m";
break;
case XmLARGE_ICON_SIZE:
iconSubs[M_SUB].substitution = ".l";
break;
case XmUNSPECIFIED_ICON_SIZE:
iconSubs[M_SUB].substitution = NULL;
break;
}
iconSubs[H_SUB].substitution = hostPrefix;
if (useIconFileCache)
testFileFunc = TestIconFile;
else
testFileFunc = NULL;
names[0] = imageInstanceName;
names[1] = imageClassName;
names_w_size[0] = names_w_size[1] = (String)NULL;
/** loop over the two names */
for (i = 0; i < 2; i++) {
if (names[i] == NULL)
continue;
if ((absolute = _XmOSAbsolutePathName(names[i], &names[i],
stackString)) != FALSE) {
iPath = ABSOLUTE_IPATH;
bPath = ABSOLUTE_PATH;
}
else {
iPath = iconPath;
bPath = bmPath;
}
iconSubs[B_SUB].substitution = names[i];
iconSubs[P_SUB].substitution = names[i];
/* need to add size suffix if size is specified */
if (size != XmUNSPECIFIED_ICON_SIZE) {
int basenameLen = strlen(names[i]);
int sizeLen = strlen(iconSubs[M_SUB].substitution);
char * ext_name = XtMalloc(basenameLen + sizeLen + 1);
/* XmosP.h takes care of bcopy translation */
memmove(&ext_name[0], names[i], basenameLen);
memmove(&ext_name[basenameLen],
iconSubs[M_SUB].substitution, sizeLen);
ext_name[basenameLen + sizeLen] = '\0';
names_w_size[i] = ext_name;
} else
names_w_size[i] = NULL;
/*
* try to see if its already in the image cache
*/
if (_XmInImageCache(names[i]))
fileName = XtNewString(names[i]);
/*
* optimization to check all expansions in cache
*/
if (!fileName) {
DtIconNameEntry iNameEntry;
DtIconNameEntryRec iNameData ;
iNameData.key_name = (names_w_size[i])?names_w_size[i]:names[i];
iNameEntry = (DtIconNameEntry)
_XmGetHashEntry(iconNameCache, (XmHashKey)&iNameData);
if (iNameEntry) {
int dirLen, leafLen;
dirLen = strlen(iNameEntry->dirName);
leafLen = strlen(iNameEntry->leafName);
fileName = XtMalloc(dirLen + leafLen + 2);
memmove(&fileName[0],
iNameEntry->dirName,
dirLen);
#ifdef FIX_1427
if (dirLen == 0) {
memmove(&fileName[dirLen], iNameEntry->leafName, leafLen);
fileName[dirLen + leafLen] = '\0';
} else {
#endif
fileName[dirLen] = '/';
memmove(&fileName[dirLen + 1],
iNameEntry->leafName,
leafLen);
fileName[dirLen + leafLen + 1] = '\0';
#ifdef FIX_1427
}
#endif
}
}
if (fileName) {
/*
* CDExc20823 (memory leak): free names_w_size[i]
* if it is not NULL.
* NOTE: This code could be reorganized to do
* _XmInImageCache() at the top of this loop
* so we could avoid unnecessary malloc's for
* names_w_size, but I wanted to minimize the
* code impact of this defect for now.
*/
for (i = 0; i < 2; i++)
{
if (names_w_size[i] != (String)NULL)
XtFree(names_w_size[i]);
}
_XmProcessUnlock();
_XmAppUnlock(app);
return fileName;
}
/*******************************
* first try XPM and then XBM
******************************/
fileName =
XtResolvePathname(display, "icons", NULL,
NULL, iPath, iconSubs,
XtNumber(iconSubs),
(XtFilePredicate) testFileFunc);
if (fileName == NULL) {
fileName =
XtResolvePathname(display, "bitmaps", NULL,
NULL, bPath, iconSubs,
XtNumber(iconSubs),
(XtFilePredicate) testFileFunc);
}
if (fileName)
break;
}
_XmProcessUnlock();
if (fileName && !absolute) {
/* register it in name cache */
DtIconNameEntry iNameEntry;
String name_used = (names_w_size[i])? names_w_size[i] : names[i] ;
/** alloc a icon cache entry **/
iNameEntry = (DtIconNameEntry) XtMalloc(sizeof(DtIconNameEntryRec));
iNameEntry->key_name = XtNewString(name_used);
#ifndef XTHREADS
if (useIconFileCache)
{
iNameEntry->dirName = XtNewString(GdirName);
iNameEntry->leafName = XtNewString(GleafName);
}
else
#endif
{
String dirName;
String filePtr;
String suffixPtr;
int dirNameLen;
_XmOSFindPathParts(fileName, &filePtr, &suffixPtr);
if (fileName == filePtr)
dirNameLen = 0;
else {
/* take the slash into account */
dirNameLen = filePtr - fileName - 1;
}
dirName = (String)XtMalloc(dirNameLen + 1);
strncpy(dirName, fileName, dirNameLen);
dirName[dirNameLen] = '\0';
iNameEntry->dirName = dirName;
iNameEntry->leafName = XtNewString(filePtr);
}
_XmProcessLock();
_XmAddHashEntry(iconNameCache, (XmHashKey)iNameEntry,
(XtPointer)iNameEntry);
_XmProcessUnlock();
}
/*
* CDExc20823 (memory leak): free names_w_size[i] if not NULL.
*/
for (i = 0; i < 2; i++)
{
if (names_w_size[i] != (String)NULL)
XtFree(names_w_size[i]);
}
_XmAppUnlock(app);
return fileName;
}