1 /*
2  * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2008, 2011,
3  *	2014
4  *	Tama Communications Corporation
5  * #ifdef __DJGPP__
6  * Contributed by Jason Hood <jadoxa@yahoo.com.au>, 2001.
7  # #endif
8  *
9  * This file is part of GNU GLOBAL.
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #include <strings.h>
32 #endif
33 #ifdef STDC_HEADERS
34 #include <stdlib.h>
35 #endif
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 
42 #ifdef __DJGPP__
43 #include <fcntl.h>			/* for _USE_LFN */
44 #endif
45 
46 #include "gparam.h"
47 #include "path.h"
48 #include "strbuf.h"
49 #include "strlimcpy.h"
50 #include "test.h"
51 
52 #if defined(_WIN32) && !defined(__CYGWIN__)
53 #define mkdir(path,mode) mkdir(path)
54 #endif
55 
56 
57 /**
58  * isabspath: whether absolute path or not
59  *
60  *	@param[in]	p	path
61  *	@return		1: absolute, 0: not absolute
62  */
63 int
isabspath(const char * p)64 isabspath(const char *p)
65 {
66 	if (p[0] == '/')
67 		return 1;
68 #if defined(_WIN32) || defined(__DJGPP__)
69 	if (p[0] == '\\')
70 		return 1;
71 	if (isdrivechar(p[0]) && p[1] == ':' && (p[2] == '\\' || p[2] == '/'))
72 		return 1;
73 #endif
74 	return 0;
75 }
76 
77 /**
78  * canonpath: make canonical path name.
79  *
80  *	@param[in,out]	path	path
81  *	@return		path
82  *
83  * canonpath() rewrite argument buffer.
84  */
85 char *
canonpath(char * path)86 canonpath(char *path)
87 {
88 #ifdef __DJGPP__
89 	char *p;
90 
91 	if (_USE_LFN) {
92 		char name[260], sfn[13];
93 		char *base;
94 
95 		/*
96 		 * Ensure we're using a complete long name, not a mixture
97 		 * of long and short.
98 		 */
99 		_truename(path, path);
100 		/*
101 		 * _truename will successfully convert the path of a non-
102 		 * existant file, but it's probably still a mixture of long and
103 		 * short components - convert the path separately.
104 		 */
105 		if (access(path, F_OK) != 0) {
106 			base = basename(path);
107 			strcpy(name, base);
108 			*base = '\0';
109 			_truename(path, path);
110 			strcat(path, name);
111 		}
112 		/*
113 		 * Convert the case of 8.3 names, as other djgpp functions do.
114 		 */
115 		if (!_preserve_fncase()) {
116 			for (p = path+3, base = p-1; *base; p++) {
117 				if (*p == '\\' || *p == '\0') {
118 					memcpy(name, base+1, p-base-1);
119 					name[p-base-1] = '\0';
120 					if (!strcmp(_lfn_gen_short_fname(name, sfn), name)) {
121 						while (++base < p)
122 							if (*base >= 'A' && *base <= 'Z')
123 								*base += 'a' - 'A';
124 					} else
125 					   base = p;
126 				}
127 			}
128 		}
129 	}
130 	/*
131 	 * Lowercase the drive letter and convert to slashes.
132 	 */
133 	path[0] = tolower(path[0]);
134 	for (p = path+2; *p; ++p)
135 		if (*p == '\\')
136 			*p = '/';
137 #else
138 #ifdef _WIN32
139 	char *p, *s;
140 	p = path;
141 	/*
142 	 * Change \ to / in a path (for DOS/Windows paths)
143 	 */
144 	while ((p = strchr(p, '\\')) != NULL)
145 		*p = '/';
146 #ifdef __CYGWIN__
147 	/*
148 	 * On NT with CYGWIN, getcwd can return something like
149 	 * "//c/tmp", which isn't usable. We change that to "c:/tmp".
150 	 */
151 	p = path;
152 	if (p[0] == '/' && p[1] == '/' && isdrivechar(p[2]) && p[3] == '/') {
153 		s = &p[2];		/* point drive char */
154 		*p++ = *s++;
155 		*p++ = ':';
156 		while (*p++ = *s++)
157 			;
158 	}
159 #endif /* __CYGWIN__ */
160 #endif /* _WIN32 */
161 #endif /* __DJGPP__ */
162 	return path;
163 }
164 
165 #if (defined(_WIN32) && !defined(__CYGWIN__)) || defined(__DJGPP__)
166 #include "checkalloc.h"
167 /**
168  * realpath: get the complete path
169  *
170  * @param[in]	in_path
171  * @param[out]	out_path	result string
172  * @return	out_path
173  */
174 char *
realpath(const char * in_path,char * out_path)175 realpath(const char *in_path, char *out_path)
176 {
177 	if (out_path == NULL)
178 		out_path = check_malloc(MAXPATHLEN);
179 #ifdef __DJGPP__
180 	/*
181 	 * I don't use _fixpath or _truename in LFN because neither guarantee
182 	 * a complete long name. This is mainly DOS's fault, since the cwd can
183 	 * be a mixture of long and short components.
184 	 */
185 	if (_USE_LFN) {
186 		strlimcpy(out_path, in_path, MAXPATHLEN);
187 		canonpath(out_path);
188 	} else
189 		_fixpath(in_path, out_path);
190 #else
191 	_fullpath(out_path, in_path, MAXPATHLEN);
192 	canonpath(out_path);
193 #endif
194 	return out_path;
195 }
196 #endif
197 
198 #define SEP '/'
199 
200 /**
201  * makedirectories: make directories on the path like mkdir(1) with the -p option.
202  *
203  *	@param[in]	base	base directory
204  *	@param[in]	rest	path from the base
205  *	@param[in]	verbose 1: verbose mode, 0: not verbose mode
206  *	@return		0: success,
207  *			-1: base directory not found,
208  *			-2: permission error,
209  *			-3: cannot make directory
210  *
211  *	Directories are created in mode 0775.
212  */
213 int
makedirectories(const char * base,const char * rest,int verbose)214 makedirectories(const char *base, const char *rest, int verbose)
215 {
216 	STRBUF *sb;
217 	const char *p, *q;
218 
219 	if (!test("d", base))
220 		return -1;
221 	if (!test("drw", base))
222 		return -2;
223 	sb = strbuf_open(0);
224 	strbuf_puts(sb, base);
225 	if (*rest == SEP)
226 		rest++;
227 	for (q = rest; *q;) {
228 		p = q;
229 		while (*q && *q != SEP)
230 			q++;
231 		strbuf_putc(sb, SEP);
232 		strbuf_nputs(sb, p, q - p);
233 		p = strbuf_value(sb);
234 		if (!test("d", p)) {
235 			if (verbose)
236 				fprintf(stderr, " Making directory '%s'.\n", p);
237 			if (mkdir(p, 0775) < 0) {
238 				strbuf_close(sb);
239 				return -3;
240 			}
241 		}
242 		if (*q == SEP)
243 			q++;
244 	}
245 	strbuf_close(sb);
246 	return 0;
247 }
248 /**
249  * trimpath: trim path name
250  *
251  * Just skips over "./" at the beginning of path, if present.
252  */
253 const char *
trimpath(const char * path)254 trimpath(const char *path)
255 {
256 	if (*path == '.' && *(path + 1) == '/')
257 		path += 2;
258 	return path;
259 }
260 /**
261  * get the current directory
262  *
263  * @param[out]	buf	result string
264  * @param[in]	size	size of buf
265  * @return	buf or NULL
266  */
267 char *
vgetcwd(char * buf,size_t size)268 vgetcwd(char *buf, size_t size) {
269 	char *p;
270 
271 	if (getenv("GTAGSLOGICALPATH")) {
272 		if ((p = getenv("PWD")) != NULL) {
273 			strlimcpy(buf, p, size);
274 			return buf;
275 		}
276 	}
277 	if (getcwd(buf, size) != NULL)
278 		return buf;
279 	return NULL;
280 }
281