tentative fix for issue 3 (ex 53)
[mplib] / src / texk / kpathsea / xgetcwd.c
1 /* xgetcwd.c: a from-scratch version of getwd.  Ideas from the tcsh 5.20
2    source, apparently uncopyrighted.
3
4    Copyright 2005         Olaf Weber.
5    Copyright 1992, 94, 96 Karl Berry.
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include <kpathsea/config.h>
23
24 #if defined (HAVE_GETCWD) || defined (HAVE_GETWD)
25 #include <kpathsea/c-pathmx.h>
26 #else /* not HAVE_GETCWD && not HAVE_GETWD*/
27 #include <kpathsea/c-dir.h>
28 #include <kpathsea/xopendir.h>
29 #include <kpathsea/xstat.h>
30
31
32 static void
33 xchdir P1C(string, dirname)
34 {
35     if (chdir(dirname) != 0)
36         FATAL_PERROR(dirname);
37 }
38
39 #endif /* not HAVE_GETCWD && not HAVE_GETWD */
40
41
42 /* Return the pathname of the current directory, or give a fatal error.  */
43
44 string
45 xgetcwd P1H(void)
46 {
47     /* If the system provides getcwd, use it.  If not, use getwd if
48        available.  But provide a way not to use getcwd: on some systems
49        getcwd forks, which is expensive and may in fact be impossible for
50        large programs like tex.  If your system needs this define and it
51        is not detected by configure, let me know.
52                                        -- Olaf Weber <infovore@xs4all.nl */
53 #if defined (HAVE_GETCWD) && !defined (GETCWD_FORKS)
54     string path = (string)xmalloc(PATH_MAX + 1);
55   
56     if (getcwd (path, PATH_MAX + 1) == 0) {
57         fprintf(stderr, "getcwd: %s", path);
58         exit(EXIT_FAILURE);
59     }
60     
61     return path;
62 #elif defined (HAVE_GETWD)
63     string path = (string)xmalloc(PATH_MAX + 1);
64     
65     if (getwd (path) == 0) {
66         fprintf(stderr, "getwd: %s", path);
67         exit(EXIT_FAILURE);
68     }
69   
70     return path;
71 #else /* not HAVE_GETCWD && not HAVE_GETWD */
72     struct stat root_stat, cwd_stat;
73     string cwd_path = (string)xmalloc(2); /* In case we assign "/" below.  */
74   
75     *cwd_path = 0;
76   
77     /* Find the inodes of the root and current directories.  */
78     root_stat = xstat("/");
79     cwd_stat  = xstat(".");
80
81     /* Go up the directory hierarchy until we get to root, prepending each
82        directory we pass through to `cwd_path'.  */
83     while (!SAME_FILE_P(root_stat, cwd_stat)) {
84         struct dirent *e;
85         DIR *parent_dir;
86         boolean found = false;
87         
88         xchdir("..");
89         parent_dir = xopendir(".");
90
91         /* Look through the parent directory for the entry with the same
92            inode, so we can get its name.  */
93         while ((e = readdir (parent_dir)) != NULL && !found) {
94             struct stat test_stat;
95             test_stat = xlstat(e->d_name);
96             
97             if (SAME_FILE_P(test_stat, cwd_stat)) {
98                 /* We've found it.  Prepend the pathname.  */
99                 string temp = cwd_path;
100                 cwd_path = concat3("/", e->d_name, cwd_path);
101                 free(temp);
102               
103                 /* Set up to test the next parent.  */
104                 cwd_stat = xstat(".");
105               
106                 /* Stop reading this directory.  */
107                 found = true;
108             }
109         }
110         if (!found)
111             FATAL2("No inode %d/device %d in parent directory",
112                    cwd_stat.st_ino, cwd_stat.st_dev);
113       
114         xclosedir(parent_dir);
115     }
116   
117     /* If the current directory is the root, cwd_path will be the empty
118        string, and we will have not gone through the loop.  */
119     if (*cwd_path == 0)
120         strcpy(cwd_path, "/");
121     else
122         /* Go back to where we were.  */
123         xchdir(cwd_path);
124
125 #ifdef DOSISH
126     /* Prepend the drive letter to CWD_PATH, since this technique
127        never tells us what the drive is.
128  
129        Note that on MS-DOS/MS-Windows, the branch that works around
130        missing `getwd' will probably only work for DJGPP (which does
131        have `getwd'), because only DJGPP reports meaningful
132        st_ino numbers.  But someday, somebody might need this...  */
133     {
134         char drive[3];
135         string temp = cwd_path;
136     
137         /* Make the drive letter lower-case, unless it is beyond Z: (yes,
138            there ARE such drives, in case of Novell Netware on MS-DOS).  */
139         drive[0] = root_stat.st_dev + (root_stat.st_dev < 26 ? 'a' : 'A');
140         drive[1] = ':';
141         drive[2] = '\0';
142         
143         cwd_path = concat(drive, cwd_path);
144         free(temp);
145     }
146 #endif
147
148     return cwd_path;
149 #endif /* not HAVE_GETCWD && not HAVE_GETWD */
150 }