/* * Motif Tools Library, Version 3.1 * $Id: FindFile.c,v 1.1.1.1 2001/07/18 11:06:02 root Exp $ * * Written by David Flanagan. * Copyright (c) 1992-2001 by David Flanagan. * All Rights Reserved. See the file COPYRIGHT for details. * This is open source software. See the file LICENSE for details. * There is no warranty for this software. See NO_WARRANTY for details. * * $Log: FindFile.c,v $ * Revision 1.1.1.1 2001/07/18 11:06:02 root * Initial checkin. * * Revision 1.2 2001/06/12 16:25:28 andre * *** empty log message *** * * */ /* * Portions of this file are derived from the Xt source code. * See the file COPYRIGHT for the MIT and Digital copyrights. */ #include #include #include /* for struct stat and stat() */ #include /* for R_OK constant */ #include #include #include /* The following procedure is modified from the X11R5 source code. */ #ifndef VMS #include #endif #ifndef SYSV386 extern struct passwd *getpwuid(), *getpwnam(); #endif #ifndef X_NOT_STDC_ENV #include #include #else extern char *getenv(); #endif #if NeedFunctionPrototypes String XmtGetHomeDir(void) #else String XmtGetHomeDir() #endif { static char *home = NULL; #ifndef VMS struct passwd *pw; #ifndef X_NOT_POSIX uid_t uid; extern uid_t getuid(); #else int uid; extern int getuid(); #endif #endif /* not VMS */ /* only do this once and cache the result */ if (home == NULL) { #ifndef VMS if (!(home = getenv("HOME"))) { if ((home = getenv("USER"))) pw = getpwnam(home); else { uid = getuid(); pw = getpwuid(uid); } if (pw) home = pw->pw_dir; else { home = ""; } } #else /* VMS */ if (!(home = getenv("HOME"))) home = getenv("sys$login"); #endif /* VMS */ if (home) home = XtNewString(home); } return home; } #ifndef VMS #ifndef NDEBUG static Boolean DebugPredicate(path) String path; { struct stat status; printf("\t%s\n", path); return (access(path, R_OK) == 0 && /* exists and is readable */ stat(path, &status) == 0 && /* get the status */ #ifndef X_NOT_POSIX S_ISDIR(status.st_mode) == 0); /* not a directory */ #else (status.st_mode & S_IFDIR) == 0); /* not a directory */ #endif /* X_NOT_POSIX else */ } #endif /* * Standard substitutions: * %R: the rootdir argument (configDir app-resource by default) * %H: the user's homedir * %A: application classname * %a: application name * %v: visual type. One of: "color", "gray", "monochrome". * %d: screen depth in bitplanes. * %z: Screen siZe: "small", "medium", "large". Based on screen reZolution. * %T: typename * %N: objname (application classname by default) * %S: suffix arg. * %C: customization * %L: language string * %l: language part * %t: territory part * %c: codeset part * %D: default Xt search path (X11R6 only) * * If a path is not specified, then the filePath app-resource is used, * or if that is not defined then the default path. Whatever the * path, this function may make up to 3 separate searches: for a user-file, * for an app file, and for a system file. * * if SearchUserPath is True, then $XUSERFILESEARCHPATH is searched first, * or if it is not defined, then the value of $XAPPLRESDIR is substituted * for %R in the default path and that path is searched first. * Then $HOME is substituted for %R in the above and that path searched. * * if SearchAppPath, or where == 0, * if the rootdir argument is non-NULL, it is substituted for %R, * and that path searched. If it is NULL, then the directory specified by * the configDir app-resource is used in its place. The default value * for this resource is the system dir: /usr/lib/X11 * * if XmtSearchSysPath is True, then XtResolvePathname is called without a * path which causes it to search in the default system places. * There's a problem: I treat suffixes differently than the Xt * default path. So if a suffix is specified, and no file is found, * try again without the suffix. */ #if NeedFunctionPrototypes String XmtFindFile(Widget w, StringConst type, StringConst objname, StringConst suffix, StringConst rootdir, StringConst path, int where) #else String XmtFindFile(w, type, objname, suffix, rootdir, path, where) Widget w; StringConst type; StringConst objname; StringConst suffix; StringConst rootdir; StringConst path; int where; #endif { static Boolean first_time = True; static String home_dir; static XtFilePredicate predicate; static SubstitutionRec subs[7]; Display *dpy; Screen *screen; Visual *visual; XmtAppResources *app_resources = XmtGetApplicationResources(w); String visual_sub; String size_sub; char depth_sub[4]; int screen_width; String filename = NULL; #ifdef X11R5 XrmDatabase old_db; #endif while(!XtIsWidget(w)) w = XtParent(w); screen = XtScreen(w); dpy = DisplayOfScreen(screen); visual = XmtGetVisual(w); /* handle absolute and relative object names */ if (objname && ((objname[0] == '/') || ((objname[0] == '.') && (objname[1] == '/')) || ((objname[0] == '.') && (objname[1] == '.') && (objname[2] == '/')))) return XtNewString(objname); /* if where is XmtSearchPathOnly, make sure there is a path */ if (where == XmtSearchPathOnly) if (path == NULL) return NULL; /* * do some one-time initialization: * Get home_dir. * Get XUSERFILESEARCHPATH; overide user_config_path if set. * Get XAPPLRESDIR; set user_config_dir to it or to homedir */ if (first_time) { String userpath, userdir; first_time = False; home_dir = XmtGetHomeDir(); userpath = getenv("XUSERFILESEARCHPATH"); if (userpath) app_resources->user_config_path = XtNewString(userpath); userdir = getenv("XAPPLRESDIR"); if (userdir) app_resources->user_config_dir = XtNewString(userdir); else app_resources->user_config_dir = home_dir; subs[0].match = 'R'; subs[1].match = 'H'; subs[2].match = 'A'; subs[3].match = 'a'; subs[4].match = 'v'; subs[5].match = 'd'; subs[6].match = 'z'; #ifndef NDEBUG if (getenv("XMTDEBUGFINDFILE")) predicate = DebugPredicate; #endif /* not NDEBUG */ } /* if no object name specified, use application class name */ if (objname == NULL) objname = app_resources->application_class; #ifndef NDEBUG if (predicate) printf("XmtFindFile: looking for object '%s' of type '%s'...\n", objname, type); #endif /* not NDEBUG */ if (visual->map_entries == 2) visual_sub = "monochrome"; else if ((visual->class == StaticGray) || (visual->class == GrayScale)) visual_sub = "gray"; else visual_sub = "color"; sprintf(depth_sub, "%d", w->core.depth); screen_width = DisplayWidth(dpy, XScreenNumberOfScreen(screen)); if (screen_width < 750) size_sub = "small"; else if (screen_width > 1150) size_sub = "large"; else size_sub = "medium"; /* set up substitutions */ /* we'll initialize the first substitution later */ subs[1].substitution = home_dir; subs[2].substitution = app_resources->application_class; subs[3].substitution = app_resources->application_name; subs[4].substitution = visual_sub; subs[5].substitution = depth_sub; subs[6].substitution = size_sub; #ifdef X11R5 /* * In R5 and later, the customization resource should come from the * screen database, but XtResolvePathname only reads the db of the * default screen. So we've got to explicitly set the right db. */ old_db = XrmGetDatabase(dpy); XrmSetDatabase(dpy, XtScreenDatabase(XtScreenOfObject(w))); #endif /* search the user paths */ if (where & XmtSearchUserPath) { /* root directory is $XAPPLRESDIR or $HOME */ subs[0].substitution = app_resources->user_config_dir; filename = XtResolvePathname(dpy, type, objname, suffix, app_resources->user_config_path, subs, XtNumber(subs), predicate); } /* * set up the root directory substitution for the rest of the searches. * root directory is the rootdir argument, or * the value of the configDir app resource. * the default value for this resource is the system directory. */ subs[0].substitution = (rootdir)?(String)rootdir:app_resources->config_dir; /* search the supplied path, if non-NULL */ if (!filename && path) { filename = XtResolvePathname(dpy, type, objname, suffix, path, subs, XtNumber(subs), predicate); } /* search the application paths, if requested */ if (!filename && (where & XmtSearchAppPath)) { filename = XtResolvePathname(dpy, type, objname, suffix, app_resources->config_path, subs, XtNumber(subs), predicate); } /* search the system paths */ if (!filename && (where & XmtSearchSysPath)) { /* * we define "system paths" to be where XtResolvePathname searches * if we don't specify a path. ie: $XFILESEARCHPATH or the default * path under /usr/lib/X11. Our special substitutions are of no * use in this case. Note that this case does not distinguish between * application names and object names. */ filename = XtResolvePathname(dpy, type, objname, suffix, NULL, NULL, 0, predicate); /* * if we didn't find anything, and there is a type and a suffix * then try again, without the suffix. This is sort of a kludge * required because programmers are expected to pass suffixes like * ".ad" and ".xbm" to this function, but files are generally not * installed with those suffixes under /usr/lib/X11. */ if (!filename && type && type[0] && suffix && suffix[0]) filename = XtResolvePathname(dpy, type, objname, NULL, NULL, NULL, 0, predicate); } #ifdef X11R5 /* * Restore the db, if needed */ XrmSetDatabase(dpy, old_db); #endif #ifndef NDEBUG if (predicate) { if (filename) printf("XmtFindFile: found '%s'.\n", filename); else printf("XmtFileFile: no file found.\n"); } #endif return filename; } #else /* VMS */ #include #include #include #include static String result_path; typedef struct file__comp { struct file__comp **next; int len; char *str; } file_comp; static Boolean VmsTestFile(char *test); static char *test_path(file_comp *path); static file_comp *breakdown(char *test); static char *assemble(file_comp *path); static void get_logical(char *logname, char ***equiv, int *n_equiv); static int check_logical(char *logname); /* ** XmtFindFile isn't all that different for VMS. The main difference is in the ** way XtResolvePathname is handled. By default on VMS it's a no-op, as it ** supplies no default path, and the file test predicate always returns true. ** We always supply a path, and we supply our own file test predicate, which is ** where all the serious work is done. Since the strings formed by ** XtResovePathname aren't legal VMS file specs, when VmsTestFile finds a file, ** it stores the actual name of it in the static variable result_path so we can ** retrieve it after XtResolvePathname returns. See below for the workings of ** that routine. ** ** We handle a couple of special cases for VMS. When searching system ** directories, if the file type is bitmap, we look in DECW$BITMAPS, and when ** it is help, we look in SYS$HELP. Everything else is looked for in the same ** place as application default files: DECW$SYSTEM_DEFAULTS. This isn't as bad ** as it seems, as that logical is a search list that includes sys$library. If ** a system manager is inclined to put files in standard VMS directories, ** that's probably where he'd put them. ** ** See appshell.c for VMS specific changes to the default path. ** ** Standard substitutions: ** %R: the rootdir argument (decw$user_defaults for user searches, ** configDir app-resource for specified path and ** application searches, and decw$system_defaults, ** decw$bitmaps, or sys$help for system searches) ** %H: the user's homedir ** %A: application classname ** %a: application name ** %T: typename ** %N: objname (application classname by default) ** %S: suffix arg. ** %C: customization ** %L: language string ** %l: language part ** %t: territory part ** %c: codeset part ** ** Note that this function may make up to 4 separate searches: for a user-file, ** a file with the specified path, for an app file, and for a system file. ** ** if SearchUserPath is True, then DECW$USER_DEFAULTS is searched first. ** ** Then, if a path was supplied, that path is searched using the specified root ** directory, or the configDir app resource if no root directory was specified. ** ** if SearchAppPath, or where == 0, if the rootdir argument is non-NULL, it is ** substituted for %R, otherwise the value of the configDir resource (which ** defaults to DECW$SYSTEM_DEFAULTS) is substituted for %R. Then the default ** path, or the path specified by the configPath resource, is searched. The ** default value for configPath is found in appshell.c. ** ** if XmtSearchSysPath is True, we search the system directories using a ** special path that looks only in the default system directory, and looks for ** the object name with and without the suffix. The default system directory ** for bitmaps (type == "bitmap") is DECW$BITMAPS. The default system directory ** for help files (type == "help") is SYS$HELP. The default system directory ** for everything else is DECW$SYSTEM_DEFAULTS, which, by default, searches ** several DECwindows directories, followed by sys$library. ** ** NOTE: We handle a special case for use by mockup that avoids initializing or ** using application resources so that this routine can be called during ** application initialization without automatically initializing anything. This ** is a gross hack, made necessary by the fact that VMS DECwindows/Motif doesn't ** give us any way to force a particular file to be used for the app-defaults ** file, which is precisely what mockup needs to do. ** ** If where is equal to XmtSearchEverywhere+1, we are in "mockup mode". We ** reset where to XmtSearchPathOnly, we do NOT initialize the app_resources ** variable, and we don't execute any code that might reference it. This is ** because XmtGetApplicationResources has some first time code that will ** intialize the app resources and we don't want that to happen till we play ** some games with the resource database (see mockup.c). We also don't execute ** XmtFindFile's own first_time code, for similar reasons. Note that the ** supplied path (which must exist) can not include any substitutions but the ** standard ones supported by XtResolvePathname and %R (the user's current ** default directory). */ #if NeedFunctionPrototypes String XmtFindFile(Widget w, StringConst type, StringConst objname, StringConst suffix, StringConst rootdir, StringConst path, int where) #else String XmtFindFile(w, type, objname, suffix, rootdir, path, where) Widget w; StringConst type; StringConst objname; StringConst suffix; StringConst rootdir; StringConst path; int where; #endif { XmtAppResources *app_resources; static Boolean first_time = True; static String home_dir; SubstitutionRec subs[4]; Display *dpy = XtDisplayOfObject(w); String filename = NULL; static XrmQuark bitmapQ, helpQ; XrmQuark typeQ; int mockup=0; /* Check for mockup mode. */ if(where == (XmtSearchEverywhere)+1){ mockup = 1; where = XmtSearchPathOnly; subs[0].match = 'R'; subs[0].substitution = (String)rootdir; } else app_resources = XmtGetApplicationResources(w); /* handle absolute object names */ if (objname && (strchr(objname, '[') || strchr(objname, ':') || check_logical(objname))) return XtNewString(objname); /* if where is XmtSearchPathOnly, make sure there is a path */ if (where == XmtSearchPathOnly) if (path == NULL) return NULL; result_path = NULL; if(!mockup){ /* * do some one-time initialization: * Get home_dir. * Get XUSERFILESEARCHPATH; overide user_config_path if set. * Get XAPPLRESDIR; set user_config_dir to it or to homedir */ if (first_time) { first_time = False; home_dir = XmtGetHomeDir(); app_resources->user_config_dir = "decw$user_defaults:"; bitmapQ = XrmStringToQuark("bitmap"); helpQ = XrmStringToQuark("help"); } /* if no object name specified, use application class name */ if (objname == NULL) objname = app_resources->application_class; /* set up substitutions */ subs[0].match = 'R'; /* we'll initialize this substitution later */ subs[1].match = 'H'; subs[1].substitution = home_dir; subs[2].match = 'A'; subs[2].substitution=app_resources->application_class; subs[3].match = 'a'; subs[3].substitution=app_resources->application_name; } /* search the user paths */ if (where & XmtSearchUserPath) { /* root directory is $XAPPLRESDIR or $HOME */ subs[0].substitution = app_resources->user_config_dir; filename = XtResolvePathname(dpy, type, objname, suffix, app_resources->user_config_path, subs, XtNumber(subs), VmsTestFile); } /* * set up the root directory substitution for the rest of the searches. * root directory is the rootdir argument, or * the value of the configDir app resource. * the default value for this resource is the system directory. */ if(!mockup)subs[0].substitution = (rootdir)?(String)rootdir:app_resources->config_dir; /* search the supplied path, if non-NULL */ if (!filename &&path) { filename = XtResolvePathname(dpy, type, objname, suffix, path, subs, mockup ? 1 : XtNumber(subs), VmsTestFile); } /* search the application paths, if requested */ if (!filename && (where & XmtSearchAppPath)) { filename = XtResolvePathname(dpy, type, objname, suffix, app_resources->config_path, subs, XtNumber(subs), VmsTestFile); } /* search the system paths */ if (!filename && (where & XmtSearchSysPath)) { typeQ = XrmStringToQuark(type); if(typeQ == bitmapQ)subs[0].substitution = "decw$bitmaps:"; else if(typeQ == helpQ)subs[0].substitution = "sys$help:"; else subs[0].substitution = "decw$system_defaults:"; /* * we define "system paths" to be where XtResolvePathname searches * if we don't specify a path. ie: $XFILESEARCHPATH or the default * path under /usr/lib/X11. Our special substitutions are of no * use in this case. Note that this case does not distinguish between * application names and object names. */ filename = XtResolvePathname(dpy, type, objname, suffix, "%R%N%S:%R%N:", subs, 1, VmsTestFile); } if(filename)XtFree(filename); return result_path; } static Boolean VmsTestFile(char *test){ file_comp *path; /* ** If a non-empty string is specified, break it down to components and test ** if the file exists. Otherwise, set result_path to NULL and return FALSE. */ if(test && *test){ path = breakdown(test); result_path = test_path(path); } else result_path = NULL; return result_path ? TRUE : FALSE; } static file_comp *breakdown(char *test){ file_comp *path = NULL, *cur; char *p, *copy, *q, *last_dot; /* ** Separate the filespec into a set of components. Delimiters are discarded. ** Note that this will break down things that bear no resemblence in form to ** VMS filespecs, and that multiple delimiters are treated as one delimiter, ** and leading delimiters are ignored. Thus ** /decw$user_defaults/[stuff]:file.type will breakdown just fine. Thus you ** don't have to worry about getting punctuation right in the paths, just as ** long as there is _some_ punctuation at the appropriate points. */ copy = XtNewString(test); last_dot = strrchr(copy, '.'); if(last_dot && !strpbrk(last_dot, ":[]/"))*last_dot = '~'; q = copy; while(p = strtok(q, ":[]/.")){ if(!path){ path = XtMalloc(sizeof(file_comp)); cur = path; q = NULL; } else { cur->next = XtMalloc(sizeof(file_comp)); cur = cur->next; } cur->str = XtNewString(p); cur->len = strlen(cur->str); } if(path)cur->next = NULL; last_dot = strchr(cur->str, '~'); if(last_dot)*last_dot = '.'; XtFree(copy); return path; } static char *assemble(file_comp *path){ int chars = 0, comps = 0, i; file_comp *p; char *ret; /* ** Assemble a filespec from the components in path. If there is only 1 ** component, simply return it. If there are 2, return comp_1:comp_2. If ** there are 3, return comp_1:[comp_2]comp_3. If there are more, we add ** subdirectories, like: comp_1:[comp_2.comp_3]comp_4. */ for(p = path; p; p = p->next){ comps++; chars += p->len; } ret = XtMalloc(chars+comps+2); strcpy(ret, path->str); if(comps > 1)strcat(ret, ":"); if(comps > 2)strcat(ret, "["); p = path->next; for(i = 1; i < comps-2; i++, p = p->next){ strcat(ret, p->str); strcat(ret, "."); } if(comps > 2){ strcat(ret, p->str); strcat(ret, "]"); p = p->next; } if(comps > 1)strcat(ret, p->str); return ret; } static char *test_path(file_comp *path){ char *ret = NULL, *test, **equiv; file_comp *log, *p, *q; int i, n_equiv; /* ** Test if the file exists and is readable. Process differently depending on ** whether the first component of the filespec is a logical name. */ get_logical(path->str, &equiv, &n_equiv); if(n_equiv){ /* ** The first component is a logical name. Loop through each equivalence ** string in order. For each one, break down the logical name and append ** all file spec components except the first, thus creating a new file ** components list that has the equivalence string substituted for the ** logical name. Then call ourselves recursively to test that path ** instead. Recursion ends when we run out of logical names to translate ** (we'll hit the else clause below). Each equivalence name is tried in ** turn as long as we haven't found a file yet. */ for(i = 0; !ret && i < n_equiv; i++){ log = breakdown(equiv[i]); for(p = log; p->next; p = p->next); p->next = path->next; ret = test_path(log); p = log; do { q = p->next; XtFree(p); p = q; } while(p != path->next); } for(i = 0; i < n_equiv; i++)XtFree(equiv[i]); XtFree(equiv); } else { /* ** No logicals, assemble a filespec and see if the file is there. */ test = assemble(path); if(access(test, 4) == 0)ret = test; else XtFree(test); } return ret; } static int check_logical(char *logname){ int n_equiv; get_logical(logname, NULL, &n_equiv); return n_equiv != 0; } static void get_logical(char *logname, char ***equiv, int *n_equiv){ typedef struct _itmlist { short blen, code; void *baddr, *rladdr; } itmlist; $DESCRIPTOR(table, "LNM$FILE_DEV"); $DESCRIPTOR(lnam, ""); int n, i, stat; short len; char str[256]; itmlist max_index[] = { {sizeof(n), LNM$_MAX_INDEX, &n, NULL}, {0, 0, 0, 0} }; itmlist value[] = { {sizeof(i), LNM$_INDEX, &i, NULL}, {255, LNM$_STRING, str, &len}, {0, 0, 0, 0} }; /* ** This routine gets all the equivalence names of the logical name logname. */ /* ** Get the number of equivalence names. */ lnam.dsc$w_length = strlen(logname); lnam.dsc$a_pointer = logname; stat = sys$trnlnm(&LNM$M_CASE_BLIND, &table, &lnam, 0, max_index); n++; if(stat != 1 || n < 1){ /* ** Not a logical or no equivalence names (Huh?) */ *n_equiv = 0; if(equiv)*equiv = NULL; } else { /* ** It is a logical and has at least one equivalence name. ** Store the number and see if the caller wants the values. */ *n_equiv = n; if(equiv){ /* ** He does. Set up equiv and loop through the equivalence names. */ *equiv = XtMalloc(*n_equiv*sizeof(char *)); for(i = 0; i < n; i++){ str[0] = '\0'; stat = sys$trnlnm(&LNM$M_CASE_BLIND, &table, &lnam, 0, value); str[len] = '\0'; (*equiv)[i] = XtNewString(str); } } } } #endif /* VMS */