experimental GDC dependencies scanner (turned off by default)
[k8jam.git] / src / pathsys.c
blob12e7a1a2ae224afc8b492c82e670960780729d34
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3 * This file is part of Jam - see jam.c for Copyright information.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * pathsys.c - manipulate file names on UNIX, NT, OS2, AmigaOS
21 * External routines:
23 * path_parse() - split a file name into dir/base/suffix/member
24 * path_build() - build a filename given dir/base/suffix/member
25 * path_parent() - make a PATHNAME point to its parent dir
27 * File_parse() and path_build() just manipuate a string and a structure;
28 * they do not make system calls.
30 #include <limits.h>
32 #include "jam.h"
33 #include "pathsys.h"
37 * path_parse() - split a file name into dir/base/suffix/member
39 void path_parse (const char *file, PATHNAME *f) {
40 const char *p, *end;
41 memset(f, 0, sizeof(*f));
42 /* look for <grist> */
43 if (file[0] == '<' && (p = strchr(file, '>')) != NULL) {
44 f->f_grist.ptr = file;
45 f->f_grist.len = p-file;
46 file = p+1; /* 'file' moved past grist */
48 /* look for dir/ */
49 p = strrchr(file, '/');
50 #if PATH_DELIM == '\\'
51 /* on NT, look for dir\ as well */
53 char *p1 = strrchr(file, '\\');
54 p = (p1 > p ? p1 : p);
56 #endif
57 if (p != NULL) {
58 f->f_dir.ptr = file;
59 f->f_dir.len = p-file;
60 /* special case for / - dirname is /, not "" */
61 if (f->f_dir.len == 0) f->f_dir.len = 1;
62 #if PATH_DELIM == '\\'
63 /* special case for D:/ - dirname is D:/, not "D:" */
64 if (f->f_dir.len == 2 && file[1] == ':') f->f_dir.len = 3;
65 #endif
66 file = p+1; /* 'file' moved past dir */
68 end = file+strlen(file);
69 if (end > file) {
70 const char *q;
71 /* look for (member) */
72 if (end[-1] == ')' && (p = strrchr(file, '(')) != NULL) {
73 f->f_member.ptr = p+1;
74 f->f_member.len = end-p-2;
75 end = p;
77 /* look for .suffix */
78 /* this would be memrchr() */
79 p = NULL;
80 q = file;
81 while ((q = memchr(q, '.', end-q))) p = q++;
82 if (p != NULL) {
83 f->f_suffix.ptr = p;
84 f->f_suffix.len = end-p;
85 end = p;
88 /* leaves base */
89 f->f_base.ptr = file;
90 f->f_base.len = end-file;
95 * path_build() - build a filename given dir/base/suffix/member
97 void path_build (char *file, const PATHNAME *f) {
98 /* start with the grist; if the current grist isn't surrounded by <>'s, add them */
99 if (f->f_grist.len > 0) {
100 if (f->f_grist.ptr[0] != '<') *file++ = '<';
101 memmove(file, f->f_grist.ptr, f->f_grist.len);
102 file += f->f_grist.len;
103 if (file[-1] != '>') *file++ = '>';
105 /* don't prepend root if it's "." or directory is rooted */
106 if (f->f_root.len > 0 && !(f->f_root.len == 1 && f->f_root.ptr[0] == '.') && !(f->f_dir.len > 0 && f->f_dir.ptr[0] == '/')
107 #if PATH_DELIM == '\\'
108 && !(f->f_dir.len > 0 && f->f_dir.ptr[0] == '\\') && !(f->f_dir.len > 0 && f->f_dir.ptr[1] == ':')
109 #endif
112 memmove(file, f->f_root.ptr, f->f_root.len);
113 file += f->f_root.len;
114 *file++ = PATH_DELIM;
116 if (f->f_dir.len > 0) {
117 memmove(file, f->f_dir.ptr, f->f_dir.len);
118 file += f->f_dir.len;
120 /* UNIX: Put / between dir and file */
121 /* NT: Put \ between dir and file */
122 if (f->f_dir.len > 0 && (f->f_base.len > 0 || f->f_suffix.len > 0)) {
123 /* UNIX: Special case for dir \ : don't add another \ */
124 /* NT: Special case for dir / : don't add another / */
125 #if PATH_DELIM == '\\'
126 if (!(f->f_dir.len == 3 && f->f_dir.ptr[1] == ':'))
127 #endif
128 if (!(f->f_dir.len == 1 && f->f_dir.ptr[0] == PATH_DELIM)) *file++ = PATH_DELIM;
130 if (f->f_base.len > 0) {
131 memmove(file, f->f_base.ptr, f->f_base.len);
132 file += f->f_base.len;
134 if (f->f_suffix.len > 0) {
135 memmove(file, f->f_suffix.ptr, f->f_suffix.len);
136 file += f->f_suffix.len;
138 if (f->f_member.len > 0) {
139 *file++ = '(';
140 memmove(file, f->f_member.ptr, f->f_member.len);
141 file += f->f_member.len;
142 *file++ = ')';
144 *file = 0;
149 * path_parent() - make a PATHNAME point to its parent dir
151 void path_parent (PATHNAME *f) {
152 /* just set everything else to nothing */
153 f->f_base.ptr = f->f_suffix.ptr = f->f_member.ptr = "";
154 f->f_base.len = f->f_suffix.len = f->f_member.len = 0;
159 * normalize_path() - normalize a path
161 * It doesn't really generate a unique representation of a path to an entry,
162 * but at least reduces the number of categories that represent the same
163 * entry. On error, or if the supplied buffer is too small, NULL is returned.
165 char *normalize_path (const char *path, char *buffer, size_t buf_size, const char *pwd) {
166 #if PATH_DELIM == '\\'
167 /* stupid windoze; convert all idiotic backslashes to normal slashes */
168 static char w2upath[PATH_MAX];
169 #endif
170 char *res = buffer;
171 static char cwd_buf[PATH_MAX];
172 char *cwd = NULL;
173 static size_t cwd_len = 0;
174 int res_len = 0;
175 int ends_with_slash = 0;
176 /* init cwd */
177 if (pwd == NULL) {
178 if ((cwd = getcwd(cwd_buf, PATH_MAX)) == NULL) return NULL;
179 } else {
180 /*FIXME: possible overflow*/
181 snprintf(cwd_buf, sizeof(cwd_buf), "%s", pwd);
182 cwd = cwd_buf;
184 cwd_len = strlen(cwd);
185 if (path == NULL) path = "";
186 /* start reconstructing path */
187 #if PATH_DELIM == '\\'
188 snprintf(w2upath, sizeof(w2upath), "%s", path);
189 for (char *p = w2upath; *p; ++p) if (*p == '\\') *p = '/';
190 path = w2upath;
191 /* check if this path is absolute */
192 /* windoze: check if we have disk letter here */
193 if (path[0] && path[1] == ':') {
194 /* copy disk letter */
195 if (buf_size < 3) return NULL;
196 buf_size -= 2;
197 *buffer++ = (cwd[0] && cwd[1] == ':' ? cwd[0] : path[0]);
198 *buffer++ = ':';
199 path += 2;
201 /* convert cwd */
202 if (cwd[0] && cwd[1] == ':') { cwd += 2; cwd_len -= 2; }
203 for (char *p = cwd; *p; ++p) if (*p == '\\') *p = '/';
204 #endif
205 /* check if this path is absolute */
206 if (path[0] != '/') {
207 /* prepend pwd */
208 if (cwd_len > 0) {
209 if (buf_size < cwd_len+1) return NULL;
210 memmove(buffer, cwd, cwd_len);
211 res_len += cwd_len;
213 } else {
214 /* put slash in output; excess slashes will be eaten by the main loop */
215 if (buf_size < 2) return NULL;
216 buffer[res_len++] = '/';
218 ends_with_slash = (path[0] && path[strlen(path)-1] == '/');
219 /* main loop */
220 while (*path) {
221 const char *e;
222 /* skip leading slashes */
223 while (*path && path[0] == '/') ++path;
224 if (!path[0]) break; /* no more */
225 if ((e = strchr(path, '/')) == NULL) e = path+strlen(path);
226 if (e-path == 1 && path[0] == '.') {
227 /* this is unnecessary dot, skip it */
228 path = e;
229 continue;
231 if (e-path == 2 && path[0] == '.' && path[1] == '.') {
232 /* dotdot; go one dir up if we can */
233 /* we can't go up if we are at root or if previous dir is "." or ".." */
234 if (res_len > 0 && !(res_len == 1 && buffer[0] == '/')) {
235 char *pd = buffer+res_len;
236 int sz = res_len;
237 int do_remove = 0;
238 if (pd[-1] == '/') { --pd; --sz; }
239 if (sz > 0) {
240 if (pd[-1] != '.') {
241 do_remove = 1;
242 } else if (sz > 1) {
243 /* last char is '.' and we have more chars */
244 if (pd[-2] != '.') {
245 do_remove = 1;
246 } else if (sz > 2) {
247 /* last two chars is ".." and we have more chars */
248 do_remove = (pd[-3] != '/');
252 if (do_remove) {
253 if (res_len > 0 && buffer[res_len-1] == '/') --res_len; /* remove trailing slash if any */
254 while (res_len > 0 && buffer[res_len-1] != '/') --res_len; /* remove last dir */
255 path = e;
256 continue;
260 /* append this dir */
261 if (res_len > 0 && buffer[res_len-1] != '/') {
262 if (res_len+2 > buf_size) return NULL;
263 buffer[res_len++] = '/';
265 if (res_len+(e-path)+1 > buf_size) return NULL;
266 memmove(buffer+res_len, path, (e-path));
267 res_len += e-path;
268 path = e;
270 /* add trailing slash if we need it */
271 if (ends_with_slash && res_len > 0 && buffer[res_len-1] != '/') {
272 if (res_len+2 > buf_size) return NULL;
273 buffer[res_len++] = '/';
275 /* add terminator */
276 if (res_len+1 > buf_size) return NULL;
277 buffer[res_len] = 0;
278 return res;