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