2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * $TOG: DbUtil.c /main/13 1998/04/09 17:47:56 mgreess $
26 * (c) Copyright 1988, 1989, 1990, 1991, 1992, 1993
27 * by Hewlett-Packard Company, all rights reserved.
29 * (c) Copyright 1993, 1994 Hewlett-Packard Company *
30 * (c) Copyright 1993, 1994 International Business Machines Corp. *
31 * (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
32 * (c) Copyright 1993, 1994 Novell, Inc. *
37 #include <sys/types.h>
47 #include <sys/param.h> /* MAXPATHLEN, MAXHOSTNAMELEN */
49 #include <X11/Intrinsic.h>
50 #include <X11/StringDefs.h>
51 #define X_INCLUDE_DIRENT_H
52 #define XOS_USE_XT_LOCKING
53 #include <X11/Xos_r.h>
55 #include <Dt/Connect.h>
56 #include <Dt/FileUtil.h>
57 #include <Dt/DtNlUtils.h>
58 #include <Dt/Action.h>
59 #include <Dt/ActionP.h>
60 #include <Dt/ActionDbP.h>
61 #include <Dt/ActionUtilP.h>
62 #include <Dt/DbUtil.h>
63 #include <Dt/Utility.h>
65 #include <Dt/ActionDb.h>
68 /* This macro is normally defined in stat.h, but not on USL systems. */
69 # define S_ISLNK(_M) ((_M & S_IFMT)==S_IFLNK) /* test for symbolic link */
72 #ifndef CDE_INSTALLATION_TOP
73 #define CDE_INSTALLATION_TOP "/opt/dt"
76 #ifndef CDE_CONFIGURATION_TOP
77 #define CDE_CONFIGURATION_TOP "/etc/opt/dt"
84 #define FILE_INCREMENT 20
86 /* The following string holds the default value of the Dt database
87 * search path. This default search path has the following major
90 * $HOME/.dt/types[/%L] A location for the user's personal
91 * actions and filetypes.
93 * <config-location>/appconfig/types[/%L]
94 * The DT location for system-wide
97 * <top-of-dt>/types/[%L] The DT location for default
98 * system-wide actions and filetypes.
100 static char DTDATABASESEARCHPATH_DEFAULT[] =
103 CDE_CONFIGURATION_TOP "/appconfig/types/%%L,"
104 CDE_CONFIGURATION_TOP "/appconfig/types,"
105 CDE_INSTALLATION_TOP "/appconfig/types/%%L,"
106 CDE_INSTALLATION_TOP "/appconfig/types";
109 /**** Substitution records used by XtFindFile() in _DtExpandLang() ****/
110 static SubstitutionRec langSubstitutions[] =
117 static int nLangSubstitutions = XtNumber(langSubstitutions);
120 /******** Static Function Declarations ********/
122 static Boolean __testPath(
124 static void __setupLangSubstitutions(
126 static void __freeLangSubstitutions(
128 static char *_DtExpandLang(
130 static char _DtIsDir(
133 static void _DtFreeDirVector(
138 static void _DtSortFiles(
143 /******** End Static Function Declarations ********/
147 * Function Name: __testPath
151 * This function is needed by XtFindFile(). Always returns True.
155 * path = XtFindFile(..., __testPath);
160 __testPath(String str)
167 * Function Name: __setupLangSubstitutions
171 * This function initializes langSubstitutions[] for use by
176 * __setupLangSubstitutions();
181 __setupLangSubstitutions(void)
187 char *tlPtr, *ttPtr, *tcPtr, *endPtr;
190 * We should really be calling setlocale to determine the "default"
191 * locale but setlocale's return value is not standardized across
192 * the various vendor platforms nor is it consistent within differnt
193 * revs of individual OS's. (e.g. its changing between HP-UX 9.0 and
194 * HP-UX 10.0). The "right" call would be the following line:
196 * if ((lang = getenv ("LANG")) || (lang = setlocale(LC_C_TYPE,NULL)))
198 * Here we hard code the default to "C" instead of leaving it NULL.
200 languagePart = territoryPart = codesetPart = (char *)NULL;
201 if ((lang = getenv ("LANG")) == (char *)NULL)
204 lang = XtNewString(lang); /* free'd in __freeLangSubstitutions() */
207 endPtr = (char *)NULL;
208 if ((ttPtr = DtStrchr(tlPtr, '_')) != (char *)NULL)
211 if ((tcPtr = DtStrchr(ttPtr ? ttPtr : tlPtr, '.')) != (char *)NULL)
216 XtNewString(tcPtr); /* free'd in __freeLangSubstitutions() */
223 int ttLen = endPtr - ttPtr;
227 /* free'd in __freeLangSubstitutions() */
228 territoryPart = (char *)XtMalloc((ttLen + 1) * sizeof(char));
229 strncpy(territoryPart, ttPtr, ttLen);
230 territoryPart[ttLen] = '\0';
234 XtNewString(ttPtr); /* free'd in __freeLangSubstitutions() */
241 int tlLen = endPtr - tlPtr;
245 /* free'd in __freeLangSubstitutions() */
246 languagePart = (char *)XtMalloc((tlLen + 1) * sizeof(char));
247 strncpy(languagePart, tlPtr, tlLen);
248 languagePart[tlLen] = '\0';
252 XtNewString(tlPtr); /* free'd in __freeLangSubstitutions() */
254 langSubstitutions[0].substitution = lang;
255 langSubstitutions[1].substitution = languagePart;
256 langSubstitutions[2].substitution = territoryPart;
257 langSubstitutions[3].substitution = codesetPart;
262 * Function Name: __freeLangSubstitutions
266 * This function free's the strings allocated by
267 * __setupLangSubstitutions and placed into langSubstitutions[]
271 * __freeLangSubstitutions();
276 __freeLangSubstitutions(void)
280 for (i = 0; i < nLangSubstitutions; i++)
281 XtFree(langSubstitutions[i].substitution);
286 * Function Name: _DtExpandLang
290 * This function takes the string "string", and performs the following
292 * %L : contents of LANG environment variable.
293 * %l : language part of the LANG environment variable.
294 * %t : territory part of the LANG environment variable.
295 * %c : codeset part of the LANG environment variable.
296 * %% : % (e.g. %%L would be replaced by %L, and
297 * no substitution would be performed on %L)
299 * If $LANG is not defined, the $LANG is assumed to be "C".
303 * ret_string = _DtExpandLang (string);
305 * char *ret_string; Returns NULL if "string" is NULL, or it points
306 * to the expanded string.
308 * char *string; The comma-separated pathnames to expand.
310 * Note: The caller is responsible for free'ing the returned string.
321 int pathLen, maxPathLen;
323 char *tmpPtr, *tmpPtr1;
331 * We're going to use XtFindFile() to perform the replacements;
332 * the colon character ':' is used as a delimiter in XtFindFile,
333 * so we escape all colon characters in our string before
336 for (nColons = 0, tmpPtr = string;
337 (tmpPtr = DtStrchr(tmpPtr, ':')) != (char *)NULL;
343 (char *)XtCalloc(1, (strlen(string) + nColons + 1) * sizeof(char));
344 for (tmpPtr = string;
345 (tmpPtr1 = DtStrchr(tmpPtr, ':')) != (char *)NULL;
346 tmpPtr = tmpPtr1 + 1)
348 strncat(newString, tmpPtr, tmpPtr1 - tmpPtr);
349 strcat(newString, "%:");
351 strcat(newString, tmpPtr);
353 __setupLangSubstitutions();
356 * XtFindFile() assumes that the string into which it's making
357 * substitutions is a path, and therefore it assumes that the
358 * length of the string does not exceed MAXPATHLEN. Since
359 * our string is a series of paths, it CAN exceed MAXPATHLEN.
360 * So, we split our string into individual paths which we then
361 * pass off to XtFindFile().
363 pathLen = maxPathLen = 0;
364 newPath = (char *)NULL;
365 for (thisPath = DtStrtok_r(newString, ",", &tokPtr);
366 thisPath != (char *)NULL;
367 thisPath = DtStrtok_r((char *)NULL, ",", &tokPtr))
369 modPath = XtFindFile(thisPath, langSubstitutions,
370 nLangSubstitutions, __testPath);
373 char *origPath = modPath;
377 * For some reason, XtFindFile() collapses all '/'
378 * characters EXCEPT at the beginning of the path!
379 * For backwards compatibility, we collapse those here.
383 while (*(modPath + 1) == '/')
386 modLen = strlen(modPath);
388 if (pathLen + modLen + 2 > maxPathLen)
391 ((pathLen + modLen + 2 + MAXPATHLEN) / MAXPATHLEN) *
394 (char *)XtRealloc(newPath, maxPathLen * sizeof(char));
398 newPath[pathLen++] = ',';
399 strcpy(&(newPath[pathLen]), modPath);
406 __freeLangSubstitutions();
415 * Function Name: _DtIsDir
419 * This function tests a pathname to see if it is a directory.
420 * The path name is received in two pieces, which makes it easy
421 * for the calling function to test a bunch of files in a directory
422 * to see if any are subdirectories.
424 * This function does NOT handle Softbench-style pathnames with
425 * embedded hostnames.
429 * dir = _DtIsDir (path, name);
431 * char dir; Returns 0 if the item is not a directory,
433 * char *path; The first part of the pathname. Typically
434 * the directory containing the item of interest.
435 * char *name; The second half of the pathname. Typically
436 * the name of the item of interest.
445 struct stat stat_buf;
448 stat_name = XtMalloc ((Cardinal)(strlen(path) + strlen(name) + 2));
449 (void)strcpy (stat_name, path);
450 (void)strcat (stat_name, "/");
451 (void)strcat (stat_name, name);
453 if(stat (stat_name, &stat_buf))
455 stat_buf.st_mode = 0;
459 if (stat_buf.st_mode & S_IFDIR)
466 /******************************
468 * Function Name: _DtFreeDirVector
472 * This function frees a database-directory string vector.
476 * FreeDatabaseDirs (dirs);
478 * char **dirs; The string vector to free.
480 ********************************/
490 for (v = dir_vector; *v != NULL; v++)
493 XtFree ((char *)dir_vector);
498 /******************************
500 * Function Name: __swap
504 * This function exchanges two elements in an array of DtDirPaths.
510 * int i; The base index to change.
511 * DtDirPaths *data; The data to change.
513 ********************************/
522 /* The "names" field of the structure is not touched because
523 * this field is "NULL" for all of the entries.
526 data->dirs[i] = data->dirs[i+1]; data->dirs[i+1] = tmp;
528 tmp = data->paths[i];
529 data->paths[i] = data->paths[i+1]; data->paths[i+1] = tmp;
533 /******************************
535 * Function Name: _DtSortFiles
539 * Given an index, an array of "char" data and the number of elements to
540 * sort, this function sorts the data. The sorting algorithm is based
541 * on a bubble sort because the number of elements is usually less than
546 * _DtSortFiles (index, n, data);
548 * int low; The base of the array to begin the sorting.
549 * int n; The number of elements to sort.
550 * DtDirPaths *data; The data to sort.
552 ********************************/
564 * This sorting routine needs to be able to sort any portion of
565 * an array - it does not always start at element '0'.
568 for (i = low; i < (high - 1); i++)
569 for (j = low; j < (high - 1); j++)
570 #ifndef NO_MESSAGE_CATALOG
571 if ((strcoll (data->paths[j], data->paths[j+1])) > 0)
573 if ((strcmp (data->paths[j], data->paths[j+1])) > 0)
581 * Function Name: _DtFindMatchingFiles
585 * This function takes a string vector of directory names (which
586 * are in "host:/path/file" format) and a filename suffix and
587 * finds all of the files in those directories with the specified
588 * suffix. It returns a string vector of the filenames.
590 * You will typically first call _DtGetDatabaseDirPaths() to get the
593 * Use _DtFreeDatabaseDirPaths() to free up the return structure.
597 * filev = _DtFindMatchingFiles (dirs, suffix, sort_files);
599 * DtDirPaths *filev; A structure containing the names
600 * of all the files that were found.
601 * DtDirPaths *dirs; A structure of directories to be
603 * char *suffix; The suffix string which is compared
604 * to the end of the filenames. This
605 * string must contain a "." if it is
606 * part of the suffix you want to match
608 * Boolean sort_files; Should the files within a directory be sorted.
614 _DtFindMatchingFiles(
619 /* LOCAL VARIABLES */
621 register DtDirPaths *files; /* An array of pointers to the filenames which
623 int max_files; /* The total number of filenames that can be
624 stored in the "files" array before it must
626 int num_found; /* The number of files which have been found. */
627 register DIR *dirp; /* Variables for walking through the directory
630 struct dirent *dp = NULL;
632 int suffixLen, nameLen;
634 register char * next_path;
635 int files_in_this_directory;
638 _Xreaddirparams dirEntryBuf;
639 struct dirent *result;
645 files = (DtDirPaths *) XtMalloc((Cardinal)(sizeof(DtDirPaths)));
646 files->dirs = (char **) XtMalloc(sizeof(char *) * FILE_INCREMENT);
647 files->paths = (char **) XtMalloc(sizeof(char *) * FILE_INCREMENT);
648 max_files = FILE_INCREMENT;
652 /* Process each one of the directories in priority order. */
653 while (dirs->paths[nextIndex] != NULL) {
655 next_path = dirs->paths[nextIndex];
656 dirp = opendir (next_path);
659 files_in_this_directory = 0;
660 while ((result = _XReaddir(dirp, dirEntryBuf)) != NULL) {
662 /* Check the name to see if it matches the suffix and is
664 if (strlen (result->d_name) >= strlen(suffix))
666 /* Find the end of the name and compare it to the suffix. */
667 /* Get the number of chars (not bytes) in each string */
668 suffixLen = DtCharCount(suffix);
669 nameLen = DtCharCount(result->d_name);
670 file_suffix = _DtGetNthChar(result->d_name, nameLen - suffixLen);
671 if (file_suffix && (strcmp(file_suffix, suffix) == 0) &&
672 !_DtIsDir((char *)next_path, (char *)result->d_name))
675 /* The file is a match. See if there is room in the array
676 or whether we need to realloc. The "-1" is to save room
677 for the terminating NULL pointer. */
678 if (num_found == max_files - 1) {
679 files->dirs = (char **) XtRealloc ((char *)files->dirs,
680 (Cardinal)(sizeof(char *) * (max_files + FILE_INCREMENT)));
681 files->paths = (char **) XtRealloc ((char *)files->paths,
682 (Cardinal)(sizeof(char *) * (max_files + FILE_INCREMENT)));
683 max_files += FILE_INCREMENT;
686 /* Get some memory and copy the filename to the array. */
687 files->dirs[num_found] = next_file = (char *)
688 XtMalloc((Cardinal)(strlen(dirs->dirs[nextIndex]) +
689 strlen (result->d_name) + 2));
690 (void)strcpy(next_file, dirs->dirs[nextIndex]);
691 (void)strcat(next_file, "/");
692 (void)strcat(next_file, result->d_name);
694 files->paths[num_found] = next_file = (char *)
695 XtMalloc((Cardinal)(strlen(next_path) +
696 strlen (result->d_name) + 2));
697 (void)strcpy(next_file, next_path);
698 (void)strcat(next_file, "/");
699 (void)strcat(next_file, result->d_name);
702 files_in_this_directory++;
708 if (sort_files && (files_in_this_directory > 1))
709 _DtSortFiles (base, files_in_this_directory, files);
712 files->dirs[num_found] = NULL;
713 files->paths[num_found] = NULL;
718 /******************************************************************************
720 * _DtDbGetDataBaseEnv( )
721 * ------------------------
722 * This function provides a PRIVATE API for internal manipulation of the
723 * DTDATABASEDIRPATH environment variable before loading the databases.
724 * -- used by the front panel code in dtwm.
726 * If the environment variable it returns a default path.
728 * NOTE: This function returns a freshly malloc'ed string. It is up to
729 * the caller to free it.
731 ******************************************************************************/
734 _DtDbGetDataBaseEnv( void )
740 nwh_dir = getenv ("HOME");
742 * Get the DTDATABASESEARCHPATH environment variable. If it is not set,
743 * create the default value.
745 if ( temp_s = getenv ("DTDATABASESEARCHPATH"))
746 if ( *temp_s != 0 ) return XtNewString(temp_s);
749 XtMalloc((2*strlen(nwh_dir)) + strlen(DTDATABASESEARCHPATH_DEFAULT) + 1);
750 sprintf (temp_buf, DTDATABASESEARCHPATH_DEFAULT, nwh_dir, nwh_dir);
755 /******************************
757 * Function Name: _DtGetDatabaseDirPaths
761 * This function returns a structure containing the external
762 * and internal forms for all of the database directories that must be
763 * searched for Dt database files.
764 * The structure is freed using _DtFreeDatabaseDirPaths().
766 * The directories are all guaranteed to be fully-specified names;
767 * i.e. host:/path/dir.
769 * THIS IS TYPICALLY CALLED BEFORE USING ANY OF THE FOLLOWING:
772 * DtPrepareToolboxDirs()
774 * _DtFindMatchingFiles()
778 * DtDirPaths * _DtGetDatabaseDirPaths ();
780 ********************************/
783 _DtGetDatabaseDirPaths( void )
785 XrmValue resource_value;
787 char *dir_string, *remote_hosts;
788 char *nwh_host; /* Holds the host portion of the user's
790 char **dir_vector; /* The list of directories are turned into
791 a vector of strings. This points to the
792 start of the vector. */
794 char **next_dir; /* A pointer used to walk through dir_vector. */
796 char *dir; /* Points to next dir being processed */
797 int valid_dirs; /* A count of the number of valid directories
801 DtDirPaths * ret_paths;
804 char *tmp_dir_string;
806 /* Get our host name, and the user's home directory */
807 nwh_host = _DtGetLocalHostName ();
808 tmp_dir_string = _DtDbGetDataBaseEnv();
809 dir_string = _DtExpandLang (tmp_dir_string);
810 XtFree (tmp_dir_string);
812 /* Prepare the input vector and the two output vectors. */
813 dir_vector = _DtVectorizeInPlace (dir_string, ',');
814 ret_paths = (DtDirPaths *)XtMalloc(sizeof(DtDirPaths));
815 ret_paths->dirs = NULL;
816 ret_paths->paths = NULL;
819 for (next_dir = dir_vector; *next_dir != NULL; next_dir++) {
821 if (DtStrchr (*next_dir, '/') == NULL){
822 /* It must be a relative path. */
823 /* Ignore relative paths */
827 /* If the name is not a valid directory, get rid of it. */
828 if (!_DtIsOpenableDirContext (*next_dir, &internal)) {
832 /* If not already in the list, add it to the structure. */
833 for (i = 0; i < valid_dirs; i++)
835 if (strcmp(ret_paths->paths[i], internal) == 0)
844 ret_paths->dirs = (char **) XtRealloc ((char *)ret_paths->dirs,
845 (Cardinal) (sizeof (char *) * valid_dirs));
847 /* Make sure the directory name is fully-qualified with a host
849 if (DtStrchr (*next_dir, ':') != NULL)
850 dir = XtNewString(*next_dir);
852 /* If there is no host component, see if there is
856 (!is_multibyte || (mblen(*next_dir, MB_LEN_MAX) == 1)) &&
858 (**next_dir == '/')) {
859 dir = XtMalloc ((Cardinal) (strlen (nwh_host) + 2 +
860 strlen (*next_dir)));
861 (void) sprintf (dir, "%s:%s", nwh_host, *next_dir);
864 dir = XtNewString(*next_dir);
866 ret_paths->dirs[valid_dirs - 1] = dir;
867 ret_paths->paths = (char **) XtRealloc ((char *)ret_paths->paths,
868 (Cardinal) (sizeof (char *) * valid_dirs));
869 ret_paths->paths[valid_dirs - 1] = internal;
877 /* The three vectors must be NULL terminated. */
878 ret_paths->dirs = (char **) XtRealloc ((char *)ret_paths->dirs,
879 (Cardinal) (sizeof (char *) *
881 ret_paths->dirs[valid_dirs] = NULL;
882 ret_paths->paths = (char **) XtRealloc ((char *)ret_paths->paths,
883 (Cardinal) (sizeof (char *) *
885 ret_paths->paths[valid_dirs] = NULL;
887 XtFree ((char *) dir_string);
888 XtFree ((char *) nwh_host);
889 XtFree ((char *) dir_vector);
894 /***************************
895 * void _DtFreeDatabaseDirPaths (paths)
897 * DtDirPaths * paths;
899 * This function will free up each of the arrays within the directory
900 * information structure, and will then free the structure itself.
902 **************************/
905 _DtFreeDatabaseDirPaths(
908 _DtFreeDirVector(paths->dirs);
909 _DtFreeDirVector(paths->paths);
910 XtFree((char *)paths);