Replace our implementation of realpath() with OpenBSD's
[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         struct stat sb;
69         int fd, n, rootd, serrno;
70         char *p, *q, wbuf[MAXPATHLEN];
71         int symlinks = 0;
72
73         /* Save the starting point. */
74         if ((fd = open(".", O_RDONLY)) < 0) {
75                 (void)strcpy(resolved, ".");
76                 return (NULL);
77         }
78
79         /*
80          * Find the dirname and basename from the path to be resolved.
81          * Change directory to the dirname component.
82          * lstat the basename part.
83          *     if it is a symlink, read in the value and loop.
84          *     if it is a directory, then change to that directory.
85          * get the current directory name and append the basename.
86          */
87         (void)strncpy(resolved, path, MAXPATHLEN - 1);
88         resolved[MAXPATHLEN - 1] = '\0';
89 loop:
90         q = strrchr(resolved, '/');
91         if (q != NULL) {
92                 p = q + 1;
93                 if (q == resolved)
94                         q = "/";
95                 else {
96                         do {
97                                 --q;
98                         } while (q > resolved && *q == '/');
99                         q[1] = '\0';
100                         q = resolved;
101                 }
102                 if (chdir(q) < 0)
103                         goto err1;
104         } else
105                 p = resolved;
106
107         /* Deal with the last component. */
108         if (lstat(p, &sb) == 0) {
109                 if (S_ISLNK(sb.st_mode)) {
110                         if (++symlinks > MAXSYMLINKS) {
111                                 errno = ELOOP;
112                                 goto err1;
113                         }
114                         n = readlink(p, resolved, MAXPATHLEN-1);
115                         if (n < 0)
116                                 goto err1;
117                         resolved[n] = '\0';
118                         goto loop;
119                 }
120                 if (S_ISDIR(sb.st_mode)) {
121                         if (chdir(p) < 0)
122                                 goto err1;
123                         p = "";
124                 }
125         }
126
127         /*
128          * Save the last component name and get the full pathname of
129          * the current directory.
130          */
131         (void)strcpy(wbuf, p);
132         if (getcwd(resolved, MAXPATHLEN) == 0)
133                 goto err1;
134
135         /*
136          * Join the two strings together, ensuring that the right thing
137          * happens if the last component is empty, or the dirname is root.
138          */
139         if (resolved[0] == '/' && resolved[1] == '\0')
140                 rootd = 1;
141         else
142                 rootd = 0;
143
144         if (*wbuf) {
145                 if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
146                         errno = ENAMETOOLONG;
147                         goto err1;
148                 }
149                 if (rootd == 0)
150                         (void)strcat(resolved, "/");
151                 (void)strcat(resolved, wbuf);
152         }
153
154         /* Go back to where we came from. */
155         if (fchdir(fd) < 0) {
156                 serrno = errno;
157                 goto err2;
158         }
159
160         /* It's okay if the close fails, what's an fd more or less? */
161         (void)close(fd);
162         return (resolved);
163
164 err1:   serrno = errno;
165         (void)fchdir(fd);
166 err2:   (void)close(fd);
167         errno = serrno;
168         return (NULL);
169 }
170
171 MODULE = Cwd            PACKAGE = Cwd
172
173 PROTOTYPES: ENABLE
174
175 void
176 fastcwd()
177 PPCODE:
178 {
179     dXSTARG;
180     sv_getcwd(TARG);
181     XSprePUSH; PUSHTARG;
182 }
183
184 void
185 abs_path(pathsv=Nullsv)
186     SV *pathsv
187 PPCODE:
188 {
189     dXSTARG;
190     char *path;
191     STRLEN len;
192     char *buf;
193
194     New(0, buf, MAXPATHLEN, char);
195     if (buf) {
196         buf[MAXPATHLEN] = 0;
197         if (pathsv)
198             path = SvPV(pathsv, len);
199         else {
200             path = ".";
201             len  = 1;
202         }
203
204         if (bsd_realpath(path, buf)) {
205             sv_setpvn(TARG, buf, strlen(buf));
206             SvPOK_only(TARG);
207         }
208         else
209             sv_setsv(TARG, &PL_sv_undef);
210
211         Safefree(buf);
212     }
213     else
214         sv_setsv(TARG, &PL_sv_undef);
215
216     XSprePUSH; PUSHTARG;
217 }