Cwd tweak from Doug MacEachern.
[p5sagit/p5-mst-13.2.git] / ext / Cwd / Cwd.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 /* The realpath() implementation from OpenBSD 2.9 (realpath.c 1.4)
6  * Renamed here to bsd_realpath() to avoid library conflicts.
7  * --jhi 2000-06-20 */
8
9 /*
10  * Copyright (c) 1994
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * This code is derived from software contributed to Berkeley by
14  * Jan-Simon Pendry.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *      This product includes software developed by the University of
27  *      California, Berkeley and its contributors.
28  * 4. Neither the name of the University nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  */
44
45 #if defined(LIBC_SCCS) && !defined(lint)
46 static char *rcsid = "$OpenBSD: realpath.c,v 1.4 1998/05/18 09:55:19 deraadt Exp $";
47 #endif /* LIBC_SCCS and not lint */
48
49 /* OpenBSD system #includes removed since the Perl ones should do. --jhi */
50
51 #ifndef MAXSYMLINKS
52 #define MAXSYMLINKS 8
53 #endif
54
55 /*
56  * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
57  *
58  * Find the real name of path, by removing all ".", ".." and symlink
59  * components.  Returns (resolved) on success, or (NULL) on failure,
60  * in which case the path which caused trouble is left in (resolved).
61  */
62 static
63 char *
64 bsd_realpath(path, resolved)
65         const char *path;
66         char *resolved;
67 {
68 #ifdef VMS
69        return Perl_rmsexpand((char*)path, resolved, NULL, 0);
70 #else
71         struct stat sb;
72         int fd, n, rootd, serrno;
73         char *p, *q, wbuf[MAXPATHLEN];
74         int symlinks = 0;
75
76         /* Save the starting point. */
77 #ifdef HAS_FCHDIR
78         if ((fd = open(".", O_RDONLY)) < 0) {
79                 (void)strcpy(resolved, ".");
80                 return (NULL);
81         }
82 #else
83         char wd[MAXPATHLEN];
84
85         if (getcwd(wd, MAXPATHLEN - 1) == NULL) {
86                 (void)strcpy(resolved, ".");
87                 return (NULL);
88         }
89 #endif
90
91         /*
92          * Find the dirname and basename from the path to be resolved.
93          * Change directory to the dirname component.
94          * lstat the basename part.
95          *     if it is a symlink, read in the value and loop.
96          *     if it is a directory, then change to that directory.
97          * get the current directory name and append the basename.
98          */
99         (void)strncpy(resolved, path, MAXPATHLEN - 1);
100         resolved[MAXPATHLEN - 1] = '\0';
101 loop:
102         q = strrchr(resolved, '/');
103         if (q != NULL) {
104                 p = q + 1;
105                 if (q == resolved)
106                         q = "/";
107                 else {
108                         do {
109                                 --q;
110                         } while (q > resolved && *q == '/');
111                         q[1] = '\0';
112                         q = resolved;
113                 }
114                 if (chdir(q) < 0)
115                         goto err1;
116         } else
117                 p = resolved;
118
119 #ifdef HAS_LSTAT
120         /* Deal with the last component. */
121         if (lstat(p, &sb) == 0) {
122                 if (S_ISLNK(sb.st_mode)) {
123                         if (++symlinks > MAXSYMLINKS) {
124                                 errno = ELOOP;
125                                 goto err1;
126                         }
127                         n = readlink(p, resolved, MAXPATHLEN-1);
128                         if (n < 0)
129                                 goto err1;
130                         resolved[n] = '\0';
131                         goto loop;
132                 }
133                 if (S_ISDIR(sb.st_mode)) {
134                         if (chdir(p) < 0)
135                                 goto err1;
136                         p = "";
137                 }
138         }
139 #endif
140
141         /*
142          * Save the last component name and get the full pathname of
143          * the current directory.
144          */
145         (void)strcpy(wbuf, p);
146         if (getcwd(resolved, MAXPATHLEN) == 0)
147                 goto err1;
148
149         /*
150          * Join the two strings together, ensuring that the right thing
151          * happens if the last component is empty, or the dirname is root.
152          */
153         if (resolved[0] == '/' && resolved[1] == '\0')
154                 rootd = 1;
155         else
156                 rootd = 0;
157
158         if (*wbuf) {
159                 if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
160                         errno = ENAMETOOLONG;
161                         goto err1;
162                 }
163                 if (rootd == 0)
164                         (void)strcat(resolved, "/");
165                 (void)strcat(resolved, wbuf);
166         }
167
168         /* Go back to where we came from. */
169 #ifdef HAS_FCHDIR
170         if (fchdir(fd) < 0) {
171                 serrno = errno;
172                 goto err2;
173         }
174 #else
175         if (chdir(wd) < 0) {
176                 serrno = errno;
177                 goto err2;
178         }
179 #endif
180
181         /* It's okay if the close fails, what's an fd more or less? */
182         (void)close(fd);
183         return (resolved);
184
185 err1:   serrno = errno;
186         (void)fchdir(fd);
187 err2:   (void)close(fd);
188         errno = serrno;
189         return (NULL);
190 #endif
191 }
192
193 MODULE = Cwd            PACKAGE = Cwd
194
195 PROTOTYPES: ENABLE
196
197 void
198 fastcwd()
199 PPCODE:
200 {
201     dXSTARG;
202     sv_getcwd(TARG);
203     XSprePUSH; PUSHTARG;
204 }
205
206 void
207 abs_path(pathsv=Nullsv)
208     SV *pathsv
209 PPCODE:
210 {
211     dXSTARG;
212     char *path;
213     STRLEN len;
214     char buf[MAXPATHLEN];
215
216     if (pathsv)
217       path = SvPV(pathsv, len);
218     else {
219         path = ".";
220         len  = 1;
221     }
222
223     if (bsd_realpath(path, buf)) {
224         sv_setpvn(TARG, buf, strlen(buf));
225         SvPOK_only(TARG);
226     }
227     else
228       sv_setsv(TARG, &PL_sv_undef);
229
230     XSprePUSH; PUSHTARG;
231 }