fixed pkg-config rule
[k8jam.git] / src / pathsys.c
blobb513be5857d7e1b249f9f0423483b5e60af989b4
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
6 /*
7 * pathsys.c - manipulate file names on UNIX, NT, OS2, AmigaOS
9 * External routines:
11 * path_parse() - split a file name into dir/base/suffix/member
12 * path_build() - build a filename given dir/base/suffix/member
13 * path_parent() - make a PATHNAME point to its parent dir
15 * File_parse() and path_build() just manipuate a string and a structure;
16 * they do not make system calls.
18 * 04/08/94 (seiwald) - Coherent/386 support added.
19 * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build()
20 * 12/19/94 (mikem) - solaris string table insanity support
21 * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way.
22 * 02/14/95 (seiwald) - parse and build /xxx properly
23 * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we
24 * should expect hdr searches to come up with strings
25 * like "thing/thing.h". So we need to test for "/" as
26 * well as "\" when parsing pathnames.
27 * 03/16/95 (seiwald) - fixed accursed typo on line 69.
28 * 05/03/96 (seiwald) - split from filent.c, fileunix.c
29 * 12/20/96 (seiwald) - when looking for the rightmost . in a file name,
30 * don't include the archive member name.
31 * 01/13/01 (seiwald) - turn off \ handling on UNIX, on by accident
32 * 11/04/02 (seiwald) - const-ing for string literals
34 #include <limits.h>
36 #include "jam.h"
37 #include "pathsys.h"
41 * path_parse() - split a file name into dir/base/suffix/member
43 void path_parse (const char *file, PATHNAME *f) {
44 const char *p, *q;
45 const char *end;
47 memset((char *)f, 0, sizeof(*f));
48 /* look for <grist> */
49 if (file[0] == '<' && (p = strchr(file, '>'))) {
50 f->f_grist.ptr = file;
51 f->f_grist.len = p-file;
52 file = p+1;
54 /* look for dir/ */
55 p = strrchr(file, '/');
56 #if PATH_DELIM == '\\'
57 /* on NT, look for dir\ as well */
59 char *p1 = strrchr(file, '\\');
60 p = p1>p?p1:p;
62 #endif
63 if (p) {
64 f->f_dir.ptr = file;
65 f->f_dir.len = p-file;
66 /* special case for / - dirname is /, not "" */
67 if (!f->f_dir.len) f->f_dir.len = 1;
68 #if PATH_DELIM == '\\'
69 /* special case for D:/ - dirname is D:/, not "D:" */
70 if (f->f_dir.len == 2 && file[1] == ':') f->f_dir.len = 3;
71 #endif
72 file = p+1;
74 end = file+strlen(file);
75 /* look for (member) */
76 if ((p = strchr(file, '(')) && end[-1] == ')') {
77 f->f_member.ptr = p+1;
78 f->f_member.len = end-p-2;
79 end = p;
81 /* look for .suffix */
82 /* this would be memrchr() */
83 p = 0;
84 q = file;
85 while ((q = (char *)memchr(q, '.', end-q))) p = q++;
86 if (p) {
87 f->f_suffix.ptr = p;
88 f->f_suffix.len = end-p;
89 end = p;
91 /* leaves base */
92 f->f_base.ptr = file;
93 f->f_base.len = end-file;
98 * path_build() - build a filename given dir/base/suffix/member
100 void path_build (PATHNAME *f, char *file, int binding) {
101 /* start with the grist; if the current grist isn't surrounded by <>'s, add them */
102 if (f->f_grist.len) {
103 if (f->f_grist.ptr[0] != '<') *file++ = '<';
104 memcpy(file, f->f_grist.ptr, f->f_grist.len);
105 file += f->f_grist.len;
106 if (file[-1] != '>') *file++ = '>';
108 /* don't prepend root if it's "." or directory is rooted */
109 #if PATH_DELIM == '/'
110 if (f->f_root.len && !(f->f_root.len == 1 && f->f_root.ptr[0] == '.') &&
111 !(f->f_dir.len && f->f_dir.ptr[0] == '/'))
112 #else /* unix */
113 if (f->f_root.len && !(f->f_root.len == 1 && f->f_root.ptr[0] == '.') &&
114 !(f->f_dir.len && f->f_dir.ptr[0] == '/') &&
115 !(f->f_dir.len && f->f_dir.ptr[0] == '\\') &&
116 !(f->f_dir.len && f->f_dir.ptr[1] == ':'))
117 #endif /* unix */
119 memcpy(file, f->f_root.ptr, f->f_root.len);
120 file += f->f_root.len;
121 *file++ = PATH_DELIM;
123 if (f->f_dir.len) {
124 memcpy(file, f->f_dir.ptr, f->f_dir.len);
125 file += f->f_dir.len;
127 /* UNIX: Put / between dir and file */
128 /* NT: Put \ between dir and file */
129 if (f->f_dir.len && (f->f_base.len || f->f_suffix.len)) {
130 /* UNIX: Special case for dir \ : don't add another \ */
131 /* NT: Special case for dir / : don't add another / */
132 #if PATH_DELIM == '\\'
133 if (!(f->f_dir.len == 3 && f->f_dir.ptr[1] == ':'))
134 #endif
135 if (!(f->f_dir.len == 1 && f->f_dir.ptr[0] == PATH_DELIM)) *file++ = PATH_DELIM;
137 if (f->f_base.len) {
138 memcpy(file, f->f_base.ptr, f->f_base.len);
139 file += f->f_base.len;
141 if (f->f_suffix.len) {
142 memcpy(file, f->f_suffix.ptr, f->f_suffix.len);
143 file += f->f_suffix.len;
145 if (f->f_member.len) {
146 *file++ = '(';
147 memcpy(file, f->f_member.ptr, f->f_member.len);
148 file += f->f_member.len;
149 *file++ = ')';
151 *file = 0;
156 * path_parent() - make a PATHNAME point to its parent dir
158 void path_parent (PATHNAME *f) {
159 /* just set everything else to nothing */
160 f->f_base.ptr = f->f_suffix.ptr = f->f_member.ptr = "";
161 f->f_base.len = f->f_suffix.len = f->f_member.len = 0;
166 * normalize_path() - normalize a path
168 * It doesn't really generate a unique representation of a path to an entry,
169 * but at least reduces the number of categories that represent the same
170 * entry. On error, or if the supplied buffer is too small, NULL is returned.
172 char *normalize_path (const char *path, char *buffer, size_t bufferSize) {
173 // init cwd
174 static char _cwd[PATH_MAX];
175 static char *cwd = 0;
176 static size_t cwdLen = 0;
177 int pathLen = path?strlen(path):0;
178 int resultLen = 0;
179 int resolveDotDot = !0;
180 // init cwd
181 if (!cwd) {
182 cwd = getcwd(_cwd, PATH_MAX);
183 if (!cwd) return 0;
184 cwdLen = strlen(cwd);
186 // check length
187 if (cwdLen+pathLen+2 > bufferSize) return 0;
188 // construct result
189 if (pathLen > 0 && path[0] == PATH_DELIM) {
190 // absolute path: ignore cwd
191 buffer[0] = PATH_DELIM;
192 buffer[1] = '\0';
193 resultLen = 1;
194 ++path;
195 --pathLen;
196 } else {
197 // relative path: copy cwd into result
198 memcpy(buffer, cwd, cwdLen+1);
199 resultLen = cwdLen;
201 // append path componentwise to the result, skipping "." and empty
202 // components, and chopping off a component per ".."
203 while (pathLen > 0) {
204 // find component
205 char *separator = strchr(path, PATH_DELIM);
206 const char *component = path;
207 int componentLen = 0;
208 if (separator) {
209 componentLen = separator-path;
210 pathLen -= componentLen+1;
211 path = separator+1;
212 } else {
213 componentLen = pathLen;
214 path += componentLen;
215 pathLen = 0;
217 // handle found component
218 if (componentLen > 0) {
219 if (componentLen == 1 && component[0] == '.') {
220 // component is ".": skip
221 } else if (resolveDotDot && componentLen == 2 && component[0] == '.' && component[1] == '.') {
222 // component is "..": eat the last component of the result
223 char *lastSeparator = strrchr(buffer, PATH_DELIM);
224 if (lastSeparator) {
225 resultLen = lastSeparator-buffer;
226 if (resultLen == 0) {
227 // always leave at least the root
228 buffer[0] = PATH_DELIM;
229 resultLen = 1;
231 buffer[resultLen] = '\0';
232 } // else: not good
233 } else {
234 // normal component: append
235 if (resultLen < 1 || buffer[resultLen-1] != PATH_DELIM) buffer[resultLen++] = PATH_DELIM;
236 memcpy(buffer+resultLen, component, componentLen);
237 resultLen += componentLen;
238 buffer[resultLen] = '\0';
239 // After we found the first real path component, we don't
240 // resolve ".." anymore, as it could be a (sym)link, which
241 // could break the algorithm.
242 resolveDotDot = 0;
246 return buffer;