1 /* @(#)abspath.c 1.5 13/10/30 Copyright 2011-2013 J. Schilling */
2 /*
3 * Compute the absolute path for a relative path name
4 *
5 * Copyright (c) 2011-2013 J. Schilling
6 */
7 /*
8 * The contents of this file are subject to the terms of the
9 * Common Development and Distribution License, Version 1.0 only
10 * (the "License"). You may not use this file except in compliance
11 * with the License.
12 *
13 * See the file CDDL.Schily.txt in this distribution for details.
14 * A copy of the CDDL is also available via the Internet at
15 * http://www.opensource.org/licenses/cddl1.txt
16 *
17 * When distributing Covered Code, include this CDDL HEADER in each
18 * file and include the License file CDDL.Schily.txt from this distribution.
19 */
20
21 #include <schily/unistd.h>
22 #include <schily/types.h>
23 #include <schily/errno.h>
24 #include <schily/maxpath.h>
25 #include <schily/string.h>
26 #include <schily/standard.h>
27 #include <schily/schily.h>
28
29 EXPORT char *abspath __PR((const char *relp, char *absp, size_t asize));
30 EXPORT char *absnpath __PR((const char *relp, char *absp, size_t asize));
31 LOCAL char *pathabs __PR((const char *relp, char *absp, size_t asize, int flags));
32 LOCAL void ashorten __PR((char *name));
33
34 /*
35 * Expands a relative pathname to a full (absolute) pathname.
36 */
37 EXPORT char *
abspath(relp,absp,asize)38 abspath(relp, absp, asize)
39 const char *relp;
40 char *absp;
41 size_t asize;
42 {
43 return (pathabs(relp, absp, asize, RSPF_EXIST));
44 }
45
46 /*
47 * Expands a relative pathname to a full (absolute) pathname.
48 * Not all path elements need to exist.
49 */
50 EXPORT char *
absnpath(relp,absp,asize)51 absnpath(relp, absp, asize)
52 const char *relp;
53 char *absp;
54 size_t asize;
55 {
56 return (pathabs(relp, absp, asize, 0));
57 }
58
59 /*
60 * Expands a relative pathname to a full (absolute) pathname.
61 * The behavior may be controlled via flags.
62 */
63 EXPORT char *
absfpath(relp,absp,asize,flags)64 absfpath(relp, absp, asize, flags)
65 const char *relp;
66 char *absp;
67 size_t asize;
68 int flags;
69 {
70 return (pathabs(relp, absp, asize, flags));
71 }
72
73 /*
74 * Expands a relative pathname to a full (absolute) pathname.
75 */
76 LOCAL char *
pathabs(relp,absp,asize,flags)77 pathabs(relp, absp, asize, flags)
78 const char *relp;
79 char *absp;
80 size_t asize;
81 int flags;
82 {
83 register char *rel;
84 register char *full;
85 int ret;
86
87 ret = resolvefpath(relp, absp, asize, flags);
88 if (ret < 0)
89 return (NULL); /* errno set by resolvepath() */
90 if (ret >= asize) {
91 seterrno(ERANGE);
92 return (NULL);
93 }
94 if (absp[0] == '/')
95 return (absp);
96
97 if (absp[0] == '.' && absp[1] == '\0')
98 return (getcwd(absp, asize)); /* Working directory. */
99
100 { int len = strlen(absp)+1;
101 #ifdef HAVE_DYN_ARRAYS
102 char tbuf[len];
103 #else
104 char *tbuf = malloc(len);
105 if (tbuf == NULL)
106 return (NULL);
107 #endif
108 strcpy(tbuf, absp);
109 absp[0] = '\0';
110 full = getcwd(absp, asize); /* Working directory. */
111
112 if (full && strlcat(full, "/", asize) >= asize) {
113 seterrno(ERANGE);
114 full = NULL;
115 }
116 if (full && strlcat(full, tbuf, asize) >= asize) {
117 seterrno(ERANGE);
118 full = NULL;
119 }
120
121 #ifndef HAVE_DYN_ARRAYS
122 free(tbuf);
123 #endif
124 if (full == NULL)
125 return (full);
126 }
127 rel = full;
128 for (;;) {
129 for (;;) {
130 rel = strchr(++rel, '/');
131 if (rel == NULL)
132 break;
133 if (rel[1] == '/' || rel[1] == '\0') {
134 *rel++ = '\0';
135 /* CSTYLED */
136 /* /foo//bar = /foo/bar */
137 while (rel[0] == '/')
138 rel++;
139 break;
140 }
141 if (rel[1] == '.') {
142 /* /foo/./bar = /foo/bar */
143 if (rel[2] == '/') {
144 *rel = '\0';
145 rel += 3;
146 break;
147 }
148 /* /foo/. = /foo */
149 if (rel[2] == '\0') {
150 *rel = '\0';
151 rel += 2;
152 break;
153 }
154 if (rel[2] == '.') {
155 /* /foo/../bar = /bar */
156 if (rel[3] == '/') {
157 *rel = '\0';
158 rel += 4;
159 ashorten(full);
160 break;
161 }
162 /* /foo/bar/.. = /foo */
163 if (rel[3] == '\0') {
164 *rel = '\0';
165 ashorten(full);
166 break;
167 }
168 }
169 }
170 }
171 if (rel == NULL || rel[0] == '\0')
172 break;
173
174 #ifdef DEBUG
175 printf("%s / %s\n", full, rel);
176 #endif
177 if (strlcat(full, "/", asize) >= asize) {
178 seterrno(ERANGE);
179 return (NULL);
180 }
181 if (strlcat(full, rel, asize) >= asize) {
182 seterrno(ERANGE);
183 return (NULL);
184 }
185 rel = full;
186 }
187 if (full[1] == '.' && full[2] == '\0') /* /. = / */
188 full[1] = '\0';
189 return (full);
190 }
191
192 /*
193 * Removes last path name component in absolute path name.
194 */
195 LOCAL void
ashorten(name)196 ashorten(name)
197 register char *name;
198 {
199 register char *p;
200
201 for (p = name++; *p++ != '\0'; );
202 while (p > name)
203 if (*--p == '/')
204 break;
205 *p = '\0';
206 }
207