]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/bmake/realpath.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / bmake / realpath.c
1 /* $Id: realpath.c,v 1.3 2013/01/25 17:06:09 sjg Exp $ */
2 /* from: $NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $       */
3
4 /*
5  * Copyright (c) 1989, 1991, 1993, 1995
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 #ifndef HAVE_REALPATH
39
40 #include <sys/cdefs.h>
41 #include <sys/param.h>
42 #include <sys/stat.h>
43
44 #include <errno.h>
45 #ifdef HAVE_STDLIB_H
46 # include <stdlib.h>
47 #endif
48 #ifdef HAVE_STRING_H
49 # include <string.h>
50 #endif
51 #ifdef HAVE_UNISTD_H
52 # include <unistd.h>
53 #endif
54
55 #ifndef __restrict
56 # define __restrict /* restrict */
57 #endif
58
59 /*
60  * char *realpath(const char *path, char *resolved);
61  *
62  * Find the real name of path, by removing all ".", ".." and symlink
63  * components.  Returns (resolved) on success, or (NULL) on failure,
64  * in which case the path which caused trouble is left in (resolved).
65  */
66 char *
67 realpath(const char * __restrict path, char * __restrict resolved)
68 {
69         struct stat sb;
70         int idx = 0, nlnk = 0;
71         const char *q;
72         char *p, wbuf[2][MAXPATHLEN], *fres;
73         size_t len;
74         ssize_t n;
75
76         /* POSIX sez we must test for this */
77         if (path == NULL) {
78                 errno = EINVAL;
79                 return NULL;
80         }
81
82         if (resolved == NULL) {
83                 fres = resolved = malloc(MAXPATHLEN);
84                 if (resolved == NULL)
85                         return NULL;
86         } else
87                 fres = NULL;
88
89
90         /*
91          * Build real path one by one with paying an attention to .,
92          * .. and symbolic link.
93          */
94
95         /*
96          * `p' is where we'll put a new component with prepending
97          * a delimiter.
98          */
99         p = resolved;
100
101         if (*path == '\0') {
102                 *p = '\0';
103                 errno = ENOENT;
104                 goto out;
105         }
106
107         /* If relative path, start from current working directory. */
108         if (*path != '/') {
109                 /* check for resolved pointer to appease coverity */
110                 if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
111                         p[0] = '.';
112                         p[1] = '\0';
113                         goto out;
114                 }
115                 len = strlen(resolved);
116                 if (len > 1)
117                         p += len;
118         }
119
120 loop:
121         /* Skip any slash. */
122         while (*path == '/')
123                 path++;
124
125         if (*path == '\0') {
126                 if (p == resolved)
127                         *p++ = '/';
128                 *p = '\0';
129                 return resolved;
130         }
131
132         /* Find the end of this component. */
133         q = path;
134         do
135                 q++;
136         while (*q != '/' && *q != '\0');
137
138         /* Test . or .. */
139         if (path[0] == '.') {
140                 if (q - path == 1) {
141                         path = q;
142                         goto loop;
143                 }
144                 if (path[1] == '.' && q - path == 2) {
145                         /* Trim the last component. */
146                         if (p != resolved)
147                                 while (*--p != '/')
148                                         continue;
149                         path = q;
150                         goto loop;
151                 }
152         }
153
154         /* Append this component. */
155         if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
156                 errno = ENAMETOOLONG;
157                 if (p == resolved)
158                         *p++ = '/';
159                 *p = '\0';
160                 goto out;
161         }
162         p[0] = '/';
163         memcpy(&p[1], path,
164             /* LINTED We know q > path. */
165             q - path);
166         p[1 + q - path] = '\0';
167
168         /*
169          * If this component is a symlink, toss it and prepend link
170          * target to unresolved path.
171          */
172         if (lstat(resolved, &sb) == -1)
173                 goto out;
174
175         if (S_ISLNK(sb.st_mode)) {
176                 if (nlnk++ >= MAXSYMLINKS) {
177                         errno = ELOOP;
178                         goto out;
179                 }
180                 n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
181                 if (n < 0)
182                         goto out;
183                 if (n == 0) {
184                         errno = ENOENT;
185                         goto out;
186                 }
187
188                 /* Append unresolved path to link target and switch to it. */
189                 if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
190                         errno = ENAMETOOLONG;
191                         goto out;
192                 }
193                 memcpy(&wbuf[idx][n], q, len + 1);
194                 path = wbuf[idx];
195                 idx ^= 1;
196
197                 /* If absolute symlink, start from root. */
198                 if (*path == '/')
199                         p = resolved;
200                 goto loop;
201         }
202         if (*q == '/' && !S_ISDIR(sb.st_mode)) {
203                 errno = ENOTDIR;
204                 goto out;
205         }
206
207         /* Advance both resolved and unresolved path. */
208         p += 1 + q - path;
209         path = q;
210         goto loop;
211 out:
212         free(fres);
213         return NULL;
214 }
215 #endif