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