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