Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / tt / lib / util / tt_path.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
24 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
25 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
26 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
27 //%%  $TOG: tt_path.C /main/9 1999/10/14 18:42:31 mgreess $                                                     
28 /* @(#)tt_path.C        1.26 93/07/30
29  * Tool Talk Utility - tt_path.cc
30  *
31  * Copyright (c) 1990, 1993 by Sun Microsystems, Inc.
32  *
33  * Implementation for filepath utility functions
34  *
35  */
36 #include <sys/types.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include "util/tt_assert.h"
42 #include "util/tt_string.h"
43 #include "util/tt_file_system_entry.h"
44 #include "util/tt_file_system.h"
45 #include "util/tt_host.h"
46 #include "util/tt_global_env.h"
47 #include "util/tt_host_equiv.h"
48
49 /* Included after "util/tt_string.h" to avoid index/strchr conflicts. */
50 #define X_INCLUDE_DIRENT_H
51 #define XOS_USE_NO_LOCKING
52 #include <X11/Xos_r.h>
53
54 #if defined(OPT_BUG_USL) || defined(OPT_BUG_UXP)
55 #define S_ISLNK(mode)   ((mode & 0xF000) == S_IFLNK)
56 #endif
57
58 // Not everybody has realpath() in libc, sometimes we have to define
59 // it ourselves, see realpath*.c in tt/lib
60
61 #if !defined(OPT_HAS_REALPATH)
62         extern "C" { char *_tt_internal_realpath(char*, char*); }
63 #endif
64
65 // Use the system realpath on OS's that have it, otherwise
66 // use the ToolTalk implementation of it.
67 char *
68 _tt_get_realpath(char  *pathname, char  *finalpath)
69 {
70 #ifdef OPT_HAS_REALPATH
71         return realpath(pathname, finalpath);
72 #else
73         return _tt_internal_realpath(pathname, finalpath);
74 #endif
75 }
76
77 /*
78  *  _Tt_dirname - given a path name, returns the char index to the end of
79  *  the path's longest prefix (directory), i.e., excluding the basename and the
80  *  last '/'.  e.g., given "/home3/dynamo/tan/text", returns 16
81  *  (/home3/dynamo/tan^).
82  */
83
84 int _Tt_dirname(const char *path, int len)
85 {
86         int path_len = len ? len : strlen(path);
87         while (path[path_len] != '/') { /* discard base name */
88                 ASSERT(path_len >= 0, "Invalid path");
89                 --path_len;
90         }
91         return (path_len - 1);          /* discard the suffix '/' */
92 }
93
94 /*
95  *  _Tt_basename - given a path name, returns the char index to the beginning of
96  *  the path's suffix, e.g., given "/home3/dynamo/tan/text", returns 18
97  *  (/home3/dynamo/tan/^).
98  */
99
100 int _Tt_basename(const char *path, int len)
101 {
102         return (_Tt_dirname(path, len) + 2);
103 }
104
105 /*
106  * _tt_dir_entries() - Return a new list of paths, with one entry for each
107  *      entry in the directory <path>, each entry consisting of
108  *      <path> appended by a slash and the name of the entry.
109  *      Returns an empty list if <path> is not a directory.
110  *      If !follow_symlinks, returns an empty list if <path> is a symlink.
111  */
112 _Tt_string_list_ptr
113 _tt_dir_entries(const _Tt_string &path, bool_t follow_symlinks )
114 {
115         DIR                    *dirp;
116         _Tt_string_list_ptr     entries(new _Tt_string_list);
117
118         if (! follow_symlinks) {
119                 struct stat     lstat_buf;
120                 int             lstat_status;
121
122                 lstat_status = lstat( (char *)path, &lstat_buf );
123                 if (( lstat_status == 0) && S_ISLNK(lstat_buf.st_mode)) {
124                         return entries;
125                 }
126         }
127         dirp = opendir( (char *)path );
128         if (dirp == NULL) {
129                 return entries;
130         }
131
132         _Tt_string      epath;
133         _Tt_string      ename;
134         _Xreaddirparams dir_buf;
135
136         memset((char*) &dir_buf, 0, sizeof(_Xreaddirparams));
137         while (TRUE) {
138                 struct dirent  *entry;
139
140                 entry = _XReaddir( dirp, dir_buf );
141                 if (entry == NULL) {
142                         break;
143                 }
144                 ename = entry->d_name;
145                 if ((ename == ".") || (ename == "..")) {
146                         continue;
147                 }
148                 epath = (char *)path;
149                 epath = epath.cat("/").cat( ename );
150                 entries->push( epath );
151         }
152         int closedir_err = closedir( dirp );
153         ASSERT(closedir_err == 0, "Could not close directory");
154         return entries;
155
156 } /* dir_entries() */
157
158 /*
159  * Returns the real path (resolves symbolic links, etc) of the specified path,
160  * even if the file in the specified path does not exist.
161  */
162 _Tt_string
163 _tt_realpath(const _Tt_string &path)
164 {
165   _Tt_string temp_path = path;
166
167   if (!path.len()) {
168     return temp_path;
169   }
170
171   if (temp_path[0] != '/') {
172     // A relative path, make it absolute.
173     char wd[MAXPATHLEN+1];
174     if (getcwd(wd, sizeof(wd))) {
175       temp_path = _Tt_string(wd).cat("/").cat(path);
176     }
177   }
178   
179   _Tt_string right, left, dir, base;
180   left = temp_path;
181   
182   _Tt_string temp_real_path(MAXPATHLEN);
183   char *p = _tt_get_realpath((char *)temp_path, (char *)temp_real_path);
184   
185   while ((p == 0) && (left.len() > 0)) {
186     // Realpath failed; drop right components until it works.
187     base = left.rsplit('/', dir);
188     
189     // Takes care of special case of "/file" where
190     // file does not exist.
191     if ((dir.len() <= 0) && (left[0]=='/')) {
192       dir = "/";
193     } 
194     left = dir;
195     
196     if (right.len() > 0) {
197       right = base.cat("/").cat(right);
198     } else {
199       right = base;
200     } 
201     p = _tt_get_realpath((char *)left, (char *)temp_real_path);
202   }
203   
204   _Tt_string real_path;
205
206   // Only "/" matches a real path...
207   if (p == 0) {
208     // Use the original path
209     real_path = temp_path;
210   }
211   // Else parts of the path don't really exist...
212   else if (right.len() > 0) {
213     // Append the fake stuff on
214     real_path = (char *)temp_real_path;
215
216     if (real_path [real_path.len()-1] != '/') {
217       real_path = real_path.cat("/").cat(right);
218     }
219     else {
220       real_path = real_path.cat(right);
221     }
222   }
223   // Else the entire path exists, now we have the "real" one...
224   else {
225     real_path = (char *)temp_real_path;
226   }
227   
228   return real_path;
229 }
230
231 int
232 _tt_isdir(const _Tt_string &path)
233 {
234         if (path.len() == 0) {
235                 return 0;
236         }
237         struct stat stat_buf;
238         if (stat((char *)path, &stat_buf) != 0) {
239                 return 0;
240         }
241         return S_ISDIR(stat_buf.st_mode);
242 }
243
244 /*
245  * Returns the the best guess of the absolute network path of the specified
246  * path without using the TT DB Server.  This is accomplished by using
247  * _tt_realpath on the specified path and then by finding best mount
248  * table entry match to the path and exchanging the local partition path
249  * with the mounted partition path.
250  */
251 _Tt_string _tt_local_network_path(const _Tt_string &path)
252 {
253         _Tt_string network_path;
254
255         if (path.len()) {
256                 _Tt_string real_path = _tt_realpath(path);
257 #ifdef notdef
258 printf("DEBUG _tt_local_network_path: real_path initialized to %s\n",
259         (char *) real_path);
260 #endif
261                 _Tt_file_system file_system;
262                 _Tt_file_system_entry_ptr entry =
263                         file_system.bestMatchToPath(real_path);
264
265                 _Tt_string hostname = entry->getHostname();
266 #ifdef notdef
267 printf("DEBUG _tt_local_network_path: hostname = %s\n",
268         (char *) hostname);
269 #endif
270                 _Tt_string loop_back_mount_point =
271                                 entry->getLoopBackMountPoint();
272 #ifdef notdef
273 printf("DEBUG _tt_local_network_path: loop_back_mount_point = %s\n",
274         (char *) loop_back_mount_point == NULL ? "(null)" : (char *) loop_back_mount_point);
275 #endif
276                 _Tt_string mount_point;
277                 if (loop_back_mount_point.len() > 0) {
278                         mount_point = loop_back_mount_point;
279 #ifdef notdef
280 printf("DEBUG _tt_local_network_path: ! isLocal 1: mount_point = %s\n",
281         (char *) mount_point);
282 #endif
283                 } else {
284                         mount_point = entry->getMountPoint();
285 #ifdef notdef
286 printf("DEBUG _tt_local_network_path: ! isLocal 2: mount_point = %s\n",
287         (char *) mount_point);
288 #endif
289                 }
290
291                 if (entry->isLocal()) {
292                         if (loop_back_mount_point.len() > 0) {
293 #ifdef notdef
294 printf("DEBUG _tt_local_network_path: isLocal 2: mount_point = %s\n",
295         (char *) mount_point);
296 #endif
297                                 // Get the path info after the loop back
298                                 // mount point path.
299                                 real_path = real_path.right(real_path.len() -
300                                                             mount_point.len());
301 #ifdef notdef
302 printf("DEBUG _tt_local_network_path: isLocal 2: real_path = %s\n",
303         (char *) real_path);
304 #endif
305                                 // Replace the loop back mount point path
306                                 // with the mount point path.
307                                 if (mount_point != "/") {
308                                         real_path = mount_point.cat(real_path);
309                                 }
310 #ifdef notdef
311 printf("DEBUG _tt_local_network_path: isLocal 3: real_path = %s\n",
312         (char *) real_path);
313 #endif
314                         }
315                 } else {
316                         _Tt_string partition = entry->getPartition();
317 #ifdef notdef
318 printf("DEBUG _tt_local_network_path: ! isLocal: partition = %s\n",
319         (char *) partition);
320 #endif
321                         // Get the path info after the mount point path
322                         real_path =
323                         real_path.right(real_path.len()-mount_point.len());
324 #ifdef notdef
325 printf("DEBUG _tt_local_network_path: ! isLocal: real_path = %s\n",
326         (char *) real_path);
327 #endif
328                         // Replace the mount point path with the exported
329                         // partition path.
330                         real_path = partition.cat(real_path);
331 #ifdef notdef
332 printf("DEBUG _tt_local_network_path: ! isLocal: real_path = %s\n",
333         (char *) real_path);
334 #endif
335                 }
336                 network_path = hostname.cat(":").cat(real_path);
337 #ifdef notdef
338 printf("DEBUG _tt_local_network_path: network_path = %s\n",
339         (char *) network_path);
340 #endif
341         }
342
343         return network_path;
344 }
345
346
347 /* Converts a path from the network form to a local form:
348  *
349  *      From:
350  *
351  *              hostname:/path
352  *
353  *      To:
354  *
355  *              /local_path
356  *
357  * If the path cannot be mapped out through the local mount tables,
358  * it converted to the form:
359  *
360  *              /net/hostname/path
361  */
362 _Tt_string
363 _tt_network_path_to_local_path(const _Tt_string &network_path)
364 {
365 // We initialize _tt_global here if necessary. When this call is exposed,
366 // we don\'t want to require that tt_open be called first.      
367         
368         if (_tt_global==0) {
369                 _tt_global = new _Tt_global;
370         }
371
372         _Tt_string local_path;
373         
374         if (!network_path.len()) {
375                 return local_path;
376         }
377         
378         _Tt_string hostname;
379         _Tt_string path;
380         _Tt_host_ptr localtthost;
381         
382         path = network_path;
383         path = path.split(':', hostname);
384
385         if (!_tt_global->get_local_host(localtthost)) {
386                 // If we don\'t even know who we are, we must not be networked
387                 // (I have no evidence this ever happens...)
388                 local_path = path;
389         } else {
390                 _Tt_host_equiv_ptr eq_p = new _Tt_host_equiv;
391
392                 if (eq_p->hostname_equiv(localtthost->name(), hostname) == 1) {
393                         // If we own the file, we can just use this path
394                         local_path = path;
395                 } else {
396                         _Tt_file_system file_system;
397                         _Tt_file_system_entry_ptr entry;
398                         entry = file_system.findMountEntry(network_path);
399                         if (entry.is_null()) {
400
401                                 char * tmp_path = getenv("DTMOUNTPOINT");
402
403                                 // Can\'t find a usable mount. In desperation,
404                                 // try to invoke the automounter.  If the
405                                 // DTMOUNTPOINT env. variable exists, use
406                                 // it as the root of the path, otherwise /net.
407
408                                 if (tmp_path == (char *)0) {
409                                         local_path = "/net/";
410                                 } else {
411                                         local_path = tmp_path;
412
413                                         // Be user friendly; ensure there
414                                         // is a trailing slash...
415                                         if (strcmp((char *) local_path.right(1), "/") != 0) {
416                                                 local_path = local_path.cat('/');
417                                         }
418                                 }
419                                 // Ensure we don't use qualified hostname.
420                                 hostname = eq_p->prefix_host(hostname,
421                                                             localtthost->name());
422
423                                 local_path = local_path.cat(hostname).cat(path);
424                         } else {
425                                 _Tt_string mount_point = entry->getMountPoint();
426                                 _Tt_string partition = entry->getPartition();
427                                 
428                                 // Get the path info after the partition path
429                                 if (strcmp((char *) partition, "/") != 0) {
430                                         path = path.right(path.len()-partition.len());
431                                 }
432                                 
433                                 // Replace the partition path with the mount point path
434                                 local_path = mount_point.cat(path);
435                         }
436                 }
437         }
438         
439         path = _tt_realpath(local_path);
440
441 #ifdef OPT_AUTOMOUNT_PATH_FIX
442         //
443         // Extract the automount point which is introduced by a
444         // call to realpath() when an NFS automounter is used:
445         //      /DTAUTOMOUNTPOINT/DTMOUNTPOINT/host/path
446         //
447         // And convert to:
448         //                       /DTMOUNTPOINT/host/path
449         //
450
451         // Fetch automount prefix
452         _Tt_string automnt_prefix = getenv("DTAUTOMOUNTPOINT");
453         if (!automnt_prefix.len()) {
454             // use default
455             automnt_prefix = "/tmp_mnt/";
456         }
457         else {
458             // fix up users prefix.   Providing an ending slash
459             // delineates the directory element.
460             if (strcmp((char *) automnt_prefix.right(1), "/") != 0) {
461                 automnt_prefix = automnt_prefix.cat('/');
462             }
463         }
464
465         // Pull out automount prefix from start of path
466         int index = path.index(automnt_prefix);
467         if (index == 0) {
468             // the path starts with the prefix, so rip it out
469             // minus the one trailing prefix slash.
470             path = path.right(path.len() - (automnt_prefix.len() - 1));
471         }
472 #endif /* OPT_AUTOMOUNT_PATH_FIX */
473
474         return path;
475 }
476
477
478 bool_t _tt_is_network_path (const _Tt_string &path)
479 {
480   int slash_index = path.index('/');
481   if (slash_index > -1) {
482     int colon_index = path.index(':');
483     if ((colon_index > 0) && (slash_index == colon_index+1)) {
484       return TRUE;
485     }
486   }  
487
488   return FALSE;
489 }
490
491 //
492 // This function looks for the specified file in the user, system and network
493 // database directories.  It returns the path of the existing file with the
494 // highest precedence (user > system > network).  If the environement variable
495 // specified by "user_env" contains the path pf a file that exists, it is
496 // the highest precedence.  If the TTPATH environment variable is specified
497 // the directories in it are used to look for the file before the standard
498 // user/system/network directories are looked at.  If "system_only" is TRUE,
499 // then only the system path is obtained (user and network are ignored).  If
500 // the returned path is a NULL string, then no file was found.
501 //
502 _Tt_string _tt_user_path (_Tt_string file,
503                           _Tt_string user_env,
504                           bool_t     system_only)
505 {
506         _Tt_string  path;
507         struct stat stat_buf;
508  
509         // If the user evnironment variable is set, that takes priority
510         if (user_env.len()) {
511                 path = getenv(user_env);
512                 if (path.len()) {
513                         if (!stat((char *)path, &stat_buf)) {
514                                 return path;
515                         }
516                 }
517
518                 path = (char *)NULL;
519         }
520
521         // Only do more of a file was specified...
522         if (file.len()) {
523                 _Tt_string tt_path = getenv("TTPATH");
524                 _Tt_string user_path = (char *)0;
525                 _Tt_string system_path = (char *)0;
526                 _Tt_string network_path = (char *)0;
527  
528                 // Parse the user:system:network from path variable
529                 int n = tt_path.index(':');
530                 if (n == -1) {
531                         user_path = tt_path;
532                         system_path = (char *)0;
533                         network_path = (char *)0;
534                 }
535                 else {   
536                         user_path = tt_path.left(n);
537                         tt_path = tt_path.right(tt_path.len() - n - 1);
538  
539                         n = tt_path.index(':');
540                         if (n == -1) {
541                                 system_path = tt_path;
542                                 network_path = (char *)0;
543                         }
544                         else {
545                                 system_path = tt_path.left(n);
546                                 tt_path = tt_path.right(tt_path.len() - n - 1);
547  
548                                 n = tt_path.index(':');
549                                 if (n != -1) {
550                                         network_path = (char *)0;
551                                 }
552                                 else {
553                                         network_path = tt_path;
554                                 }
555                         }
556                 }
557
558                 // If the paths are not directories, strip off the file at the
559                 // end of the paths.
560                 if (user_path.len() && !_tt_isdir(user_path)) {
561                         n = user_path.rindex('/');
562                         user_path = user_path.left(n);
563                 }
564                 if (system_path.len() && !_tt_isdir(system_path)) {
565                         n = system_path.rindex('/');
566                         system_path = system_path.left(n);
567                 }
568                 if (network_path.len() && !_tt_isdir(network_path)) {
569                         n = network_path.rindex('/');
570                         network_path = network_path.left(n);
571                 }
572  
573                 // Get the user file...
574                 if (!system_only) {
575                         if (user_path.len()) {
576                                 path = user_path.cat("/").cat(file);
577                                 if (!stat((char *)path, &stat_buf)) {
578                                         return path;
579                                 }
580                         }
581
582                         path = getenv("HOME");
583                         path = path.cat("/.tt/").cat(file);
584                         if (!stat((char *)path, &stat_buf)) {
585                                 return path;
586                         }
587                 }
588
589                 // Get the system file...
590                 if (system_path.len()) {
591                         path = system_path.cat("/").cat(file); 
592                         if (!stat((char *)path, &stat_buf)) { 
593                                 return path; 
594                         } 
595                 }
596
597                 path = "/etc/tt/";
598                 path = path.cat(file);
599                 if (!stat((char *)path, &stat_buf)) {
600                         return path;
601                 }
602
603                 // Get the network file...
604                 if (!system_only) {
605                         if (network_path.len()) {
606                                 path = network_path.cat("/").cat(file);  
607                                 if (!stat((char *)path, &stat_buf)) { 
608                                         return path;  
609                                 }     
610                         }
611
612                         path = getenv("OPENWINHOME");
613                         if (path.len()) {
614                                 path = path.cat("/etc/tt/").cat(file);
615                                 if (!stat((char *)path, &stat_buf)) {
616                                         return path;
617                                 }
618                         }
619
620                         path = (char *)NULL;
621                 }
622         }
623
624         return path;
625 }