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