File: FindFile.c

package info (click to toggle)
gridengine 6.2-4
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 51,532 kB
  • ctags: 51,172
  • sloc: ansic: 418,155; java: 37,080; sh: 22,593; jsp: 7,699; makefile: 5,292; csh: 4,244; xml: 2,901; cpp: 2,086; perl: 1,895; tcl: 1,188; lisp: 669; ruby: 642; yacc: 393; lex: 266
file content (782 lines) | stat: -rw-r--r-- 24,699 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
/* 
 * Motif Tools Library, Version 3.1
 * $Id$
 * 
 * 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$
 * 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 <stdio.h>
#include <sys/types.h> 
#include <sys/stat.h>  /* for struct stat and stat() */
#include <X11/Xos.h>   /* for R_OK constant */
#include <Xmt/XmtP.h>
#include <Xmt/Util.h>
#include <Xmt/AppResP.h>

/* The following procedure is modified from the X11R5 source code. */
#ifndef VMS
#include <pwd.h>
#endif

#ifndef SYSV386
extern struct passwd *getpwuid(), *getpwnam();
#endif

#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#include <unistd.h>
#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 <string.h>
#include <stdio.h>
#include <descrip.h>
#include <lnmdef.h>

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 */