|
1 // |
|
2 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
3 // |
|
4 /* |
|
5 * compat.c - compatibility routines for the deprived |
|
6 * |
|
7 * This file is part of zsh, the Z shell. |
|
8 * |
|
9 * Copyright (c) 1992-1997 Paul Falstad |
|
10 * All rights reserved. |
|
11 * |
|
12 * Permission is hereby granted, without written agreement and without |
|
13 * license or royalty fees, to use, copy, modify, and distribute this |
|
14 * software and to distribute modified versions of this software for any |
|
15 * purpose, provided that the above copyright notice and the following |
|
16 * two paragraphs appear in all copies of this software. |
|
17 * |
|
18 * In no event shall Paul Falstad or the Zsh Development Group be liable |
|
19 * to any party for direct, indirect, special, incidental, or consequential |
|
20 * damages arising out of the use of this software and its documentation, |
|
21 * even if Paul Falstad and the Zsh Development Group have been advised of |
|
22 * the possibility of such damage. |
|
23 * |
|
24 * Paul Falstad and the Zsh Development Group specifically disclaim any |
|
25 * warranties, including, but not limited to, the implied warranties of |
|
26 * merchantability and fitness for a particular purpose. The software |
|
27 * provided hereunder is on an "as is" basis, and Paul Falstad and the |
|
28 * Zsh Development Group have no obligation to provide maintenance, |
|
29 * support, updates, enhancements, or modifications. |
|
30 * |
|
31 */ |
|
32 |
|
33 #include "zsh.mdh" |
|
34 #include "compat.pro" |
|
35 |
|
36 /* Return pointer to first occurence of string t * |
|
37 * in string s. Return NULL if not present. */ |
|
38 |
|
39 #ifndef HAVE_STRSTR |
|
40 char * |
|
41 strstr(const char *s, const char *t) |
|
42 { |
|
43 char *p1, *p2; |
|
44 |
|
45 for (; *s; s++) { |
|
46 for (p1 = s, p2 = t; *p2; p1++, p2++) |
|
47 if (*p1 != *p2) |
|
48 break; |
|
49 if (!*p2) |
|
50 return (char *)s; |
|
51 } |
|
52 return NULL; |
|
53 } |
|
54 #endif |
|
55 |
|
56 |
|
57 #ifndef HAVE_GETHOSTNAME |
|
58 int |
|
59 gethostname(char *name, size_t namelen) |
|
60 { |
|
61 struct utsname uts; |
|
62 |
|
63 uname(&uts); |
|
64 if(strlen(uts.nodename) >= namelen) { |
|
65 errno = EINVAL; |
|
66 return -1; |
|
67 } |
|
68 strcpy(name, uts.nodename); |
|
69 return 0; |
|
70 } |
|
71 #endif |
|
72 |
|
73 |
|
74 #ifndef HAVE_GETTIMEOFDAY |
|
75 int |
|
76 gettimeofday(struct timeval *tv, struct timezone *tz) |
|
77 { |
|
78 tv->tv_usec = 0; |
|
79 tv->tv_sec = (long)time((time_t) 0); |
|
80 return 0; |
|
81 } |
|
82 #endif |
|
83 |
|
84 |
|
85 /* compute the difference between two calendar times */ |
|
86 |
|
87 #ifndef HAVE_DIFFTIME |
|
88 double |
|
89 difftime(time_t t2, time_t t1) |
|
90 { |
|
91 return ((double)t2 - (double)t1); |
|
92 } |
|
93 #endif |
|
94 |
|
95 |
|
96 #ifndef HAVE_STRERROR |
|
97 extern char *sys_errlist[]; |
|
98 |
|
99 /* Get error message string associated with a particular * |
|
100 * error number, and returns a pointer to that string. * |
|
101 * This is not a particularly robust version of strerror. */ |
|
102 |
|
103 char * |
|
104 strerror(int errnum) |
|
105 { |
|
106 return (sys_errlist[errnum]); |
|
107 } |
|
108 #endif |
|
109 |
|
110 |
|
111 #if 0 |
|
112 /* pathconf(_PC_PATH_MAX) is not currently useful to zsh. The value * |
|
113 * returned varies depending on a number of factors, e.g. the amount * |
|
114 * of memory available to the operating system at a given time; thus * |
|
115 * it can't be used for buffer allocation, or even as an indication * |
|
116 * of whether an attempt to use or create a given pathname may fail * |
|
117 * at any future time. * |
|
118 * * |
|
119 * The call is also permitted to fail if the argument path is not an * |
|
120 * existing directory, so even to make sense of that one must search * |
|
121 * for a valid directory somewhere in the path and adjust. Even if * |
|
122 * it succeeds, the return value is relative to the input directory, * |
|
123 * and therefore potentially relative to the length of the shortest * |
|
124 * path either to that directory or to our working directory. * |
|
125 * * |
|
126 * Finally, see the note below for glibc; detection of pathconf() is * |
|
127 * not by itself an indication that it works reliably. */ |
|
128 |
|
129 /* The documentation for pathconf() says something like: * |
|
130 * The limit is returned, if one exists. If the system does * |
|
131 * not have a limit for the requested resource, -1 is * |
|
132 * returned, and errno is unchanged. If there is an error, * |
|
133 * -1 is returned, and errno is set to reflect the nature of * |
|
134 * the error. * |
|
135 * * |
|
136 * System calls are not permitted to set errno to 0; but we must (or * |
|
137 * some other flag value) in order to determine that the resource is * |
|
138 * unlimited. What use is leaving errno unchanged? Instead, define * |
|
139 * a wrapper that resets errno to 0 and returns 0 for "the system * |
|
140 * does not have a limit," so that -1 always means a real error. */ |
|
141 |
|
142 /**/ |
|
143 mod_export long |
|
144 zpathmax(char *dir) |
|
145 { |
|
146 #ifdef HAVE_PATHCONF |
|
147 long pathmax; |
|
148 |
|
149 errno = 0; |
|
150 if ((pathmax = pathconf(dir, _PC_PATH_MAX)) >= 0) { |
|
151 /* Some versions of glibc pathconf return a hardwired value! */ |
|
152 return pathmax; |
|
153 } else if (errno == EINVAL || errno == ENOENT || errno == ENOTDIR) { |
|
154 /* Work backward to find a directory, until we run out of path. */ |
|
155 char *tail = strrchr(dir, '/'); |
|
156 while (tail > dir && tail[-1] == '/') |
|
157 --tail; |
|
158 if (tail > dir) { |
|
159 *tail = 0; |
|
160 pathmax = zpathmax(dir); |
|
161 *tail = '/'; |
|
162 } else { |
|
163 errno = 0; |
|
164 if (tail) |
|
165 pathmax = pathconf("/", _PC_PATH_MAX); |
|
166 else |
|
167 pathmax = pathconf(".", _PC_PATH_MAX); |
|
168 } |
|
169 if (pathmax > 0) { |
|
170 long taillen = (tail ? strlen(tail) : (strlen(dir) + 1)); |
|
171 if (taillen < pathmax) |
|
172 return pathmax - taillen; |
|
173 else |
|
174 errno = ENAMETOOLONG; |
|
175 } |
|
176 } |
|
177 if (errno) |
|
178 return -1; |
|
179 else |
|
180 return 0; /* pathmax should be considered unlimited */ |
|
181 #else |
|
182 long dirlen = strlen(dir); |
|
183 |
|
184 /* The following is wrong if dir is not an absolute path. */ |
|
185 return ((long) ((dirlen >= PATH_MAX) ? |
|
186 ((errno = ENAMETOOLONG), -1) : |
|
187 ((errno = 0), PATH_MAX - dirlen))); |
|
188 #endif |
|
189 } |
|
190 #endif /* 0 */ |
|
191 |
|
192 #ifdef HAVE_SYSCONF |
|
193 /* This is replaced by a macro from system.h if not HAVE_SYSCONF. * |
|
194 * 0 is returned by sysconf if _SC_OPEN_MAX is unavailable; * |
|
195 * -1 is returned on error * |
|
196 * * |
|
197 * Neither of these should happen, but resort to OPEN_MAX rather * |
|
198 * than return 0 or -1 just in case. */ |
|
199 |
|
200 /**/ |
|
201 mod_export long |
|
202 zopenmax(void) |
|
203 { |
|
204 static long openmax = 0; |
|
205 |
|
206 if (openmax < 1) { |
|
207 if ((openmax = sysconf(_SC_OPEN_MAX)) < 1) { |
|
208 openmax = OPEN_MAX; |
|
209 } else if (openmax > OPEN_MAX) { |
|
210 /* On some systems, "limit descriptors unlimited" or the * |
|
211 * equivalent will set openmax to a huge number. Unless * |
|
212 * there actually is a file descriptor > OPEN_MAX already * |
|
213 * open, nothing in zsh requires the true maximum, and in * |
|
214 * fact it causes inefficiency elsewhere if we report it. * |
|
215 * So, report the maximum of OPEN_MAX or the largest open * |
|
216 * descriptor (is there a better way to find that?). */ |
|
217 long i, j = OPEN_MAX; |
|
218 for (i = j; i < openmax; i += (errno != EINTR)) { |
|
219 errno = 0; |
|
220 if (fcntl(i, F_GETFL, 0) < 0 && |
|
221 (errno == EBADF || errno == EINTR)) |
|
222 continue; |
|
223 j = i; |
|
224 } |
|
225 openmax = j; |
|
226 } |
|
227 } |
|
228 |
|
229 return (max_zsh_fd > openmax) ? max_zsh_fd : openmax; |
|
230 } |
|
231 #endif |
|
232 |
|
233 /**/ |
|
234 mod_export char * |
|
235 zgetdir(struct dirsav *d) |
|
236 { |
|
237 char nbuf[PATH_MAX+3]; |
|
238 char *buf; |
|
239 int bufsiz, pos; |
|
240 struct stat sbuf; |
|
241 ino_t pino; |
|
242 dev_t pdev; |
|
243 #if !defined(__CYGWIN__) && !defined(USE_GETCWD) |
|
244 struct dirent *de; |
|
245 DIR *dir; |
|
246 dev_t dev; |
|
247 ino_t ino; |
|
248 int len; |
|
249 #endif |
|
250 |
|
251 buf = zhalloc(bufsiz = PATH_MAX); |
|
252 pos = bufsiz - 1; |
|
253 buf[pos] = '\0'; |
|
254 strcpy(nbuf, "../"); |
|
255 if (stat(".", &sbuf) < 0) { |
|
256 if (d) |
|
257 return NULL; |
|
258 buf[0] = '.'; |
|
259 buf[1] = '\0'; |
|
260 return buf; |
|
261 } |
|
262 |
|
263 pino = sbuf.st_ino; |
|
264 pdev = sbuf.st_dev; |
|
265 if (d) |
|
266 d->ino = pino, d->dev = pdev; |
|
267 #ifdef HAVE_FCHDIR |
|
268 else |
|
269 #endif |
|
270 #if !defined(__CYGWIN__) && !defined(USE_GETCWD) |
|
271 holdintr(); |
|
272 |
|
273 for (;;) { |
|
274 if (stat("..", &sbuf) < 0) |
|
275 break; |
|
276 |
|
277 ino = pino; |
|
278 dev = pdev; |
|
279 pino = sbuf.st_ino; |
|
280 pdev = sbuf.st_dev; |
|
281 |
|
282 if (ino == pino && dev == pdev) { |
|
283 /*if (!buf[pos]) |
|
284 buf[--pos] = '\\';*/ |
|
285 if (d) { |
|
286 #ifndef HAVE_FCHDIR |
|
287 zchdir(buf + pos); |
|
288 noholdintr(); |
|
289 #endif |
|
290 return d->dirname = ztrdup(buf + pos); |
|
291 } |
|
292 zchdir(buf + pos); |
|
293 noholdintr(); |
|
294 return buf + pos; |
|
295 } |
|
296 |
|
297 if (!(dir = opendir(".."))) |
|
298 break; |
|
299 |
|
300 while ((de = readdir(dir))) { |
|
301 char *fn = de->d_name; |
|
302 /* Ignore `.' and `..'. */ |
|
303 if (fn[0] == '.' && |
|
304 (fn[1] == '\0' || |
|
305 (fn[1] == '.' && fn[2] == '\0'))) |
|
306 continue; |
|
307 #ifdef HAVE_STRUCT_DIRENT_D_STAT |
|
308 if(de->d_stat.st_dev == dev && de->d_stat.st_ino == ino) { |
|
309 strncpy(nbuf + 3, fn, PATH_MAX); |
|
310 break; |
|
311 } |
|
312 #else /* !HAVE_STRUCT_DIRENT_D_STAT */ |
|
313 # ifdef HAVE_STRUCT_DIRENT_D_INO |
|
314 if (dev != pdev || (ino_t) de->d_ino == ino) |
|
315 # endif /* HAVE_STRUCT_DIRENT_D_INO */ |
|
316 { |
|
317 strncpy(nbuf + 3, fn, PATH_MAX); |
|
318 lstat(nbuf, &sbuf); |
|
319 if (sbuf.st_dev == dev && sbuf.st_ino == ino) |
|
320 break; |
|
321 } |
|
322 #endif /* !HAVE_STRUCT_DIRENT_D_STAT */ |
|
323 } |
|
324 closedir(dir); |
|
325 if (!de) |
|
326 break; |
|
327 len = strlen(nbuf + 2); |
|
328 pos -= len; |
|
329 while (pos <= 1) { |
|
330 char *newbuf = zhalloc(2*bufsiz); |
|
331 memcpy(newbuf + bufsiz, buf, bufsiz); |
|
332 buf = newbuf; |
|
333 pos += bufsiz; |
|
334 bufsiz *= 2; |
|
335 } |
|
336 memcpy(buf + pos, nbuf + 2, len); |
|
337 #ifdef HAVE_FCHDIR |
|
338 if (d) |
|
339 return d->dirname = ztrdup(buf + pos + 1); |
|
340 #endif |
|
341 if (chdir("..")) |
|
342 break; |
|
343 } |
|
344 if (d) { |
|
345 #ifndef HAVE_FCHDIR |
|
346 if (*buf) |
|
347 zchdir(buf + pos + 1); |
|
348 noholdintr(); |
|
349 #endif |
|
350 return NULL; |
|
351 } |
|
352 if (*buf) |
|
353 zchdir(buf + pos + 1); |
|
354 noholdintr(); |
|
355 |
|
356 #else /* __CYGWIN__, USE_GETCWD cases */ |
|
357 |
|
358 if (!getcwd(buf, bufsiz)) { |
|
359 if (d) { |
|
360 return NULL; |
|
361 } |
|
362 } else { |
|
363 if (d) { |
|
364 return d->dirname = ztrdup(buf); |
|
365 } |
|
366 return buf; |
|
367 } |
|
368 #endif |
|
369 |
|
370 buf[0] = '.'; |
|
371 buf[1] = '\0'; |
|
372 return buf; |
|
373 } |
|
374 |
|
375 /**/ |
|
376 char * |
|
377 zgetcwd(void) |
|
378 { |
|
379 return zgetdir(NULL); |
|
380 } |
|
381 |
|
382 /* chdir with arbitrary long pathname. Returns 0 on success, -1 on normal * |
|
383 * failure and -2 when chdir failed and the current directory is lost. */ |
|
384 |
|
385 /**/ |
|
386 mod_export int |
|
387 zchdir(char *dir) |
|
388 { |
|
389 char *s; |
|
390 int currdir = -2; |
|
391 #ifdef __SYMBIAN32__ |
|
392 if(dir==NULL || !*dir) |
|
393 { |
|
394 return -1; //denote failure |
|
395 } |
|
396 #endif |
|
397 |
|
398 for (;;) { |
|
399 if (!*dir || chdir(dir) == 0) { |
|
400 #ifdef HAVE_FCHDIR |
|
401 if (currdir >= 0) |
|
402 close(currdir); |
|
403 #endif |
|
404 return 0; |
|
405 } |
|
406 if ((errno != ENAMETOOLONG && errno != ENOMEM) || |
|
407 strlen(dir) < PATH_MAX) |
|
408 break; |
|
409 for (s = dir + PATH_MAX - 1; s > dir && *s != '/'; s--) |
|
410 ; |
|
411 if (s == dir) |
|
412 break; |
|
413 #ifdef HAVE_FCHDIR |
|
414 if (currdir == -2) |
|
415 currdir = open(".", O_RDONLY|O_NOCTTY); |
|
416 #endif |
|
417 *s = '\0'; |
|
418 if (chdir(dir) < 0) { |
|
419 *s = '/'; |
|
420 break; |
|
421 } |
|
422 #ifndef HAVE_FCHDIR |
|
423 currdir = -1; |
|
424 #endif |
|
425 *s = '/'; |
|
426 while (*++s == '/') |
|
427 ; |
|
428 dir = s; |
|
429 } |
|
430 #ifdef HAVE_FCHDIR |
|
431 if (currdir >= 0) { |
|
432 if (fchdir(currdir) < 0) { |
|
433 close(currdir); |
|
434 return -2; |
|
435 } |
|
436 close(currdir); |
|
437 return -1; |
|
438 } |
|
439 #endif |
|
440 return currdir == -2 ? -1 : -2; |
|
441 } |
|
442 |
|
443 /* |
|
444 * How to print out a 64 bit integer. This isn't needed (1) if longs |
|
445 * are 64 bit, since ordinary %ld will work (2) if we couldn't find a |
|
446 * 64 bit type anyway. |
|
447 */ |
|
448 /**/ |
|
449 #ifdef ZSH_64_BIT_TYPE |
|
450 /**/ |
|
451 mod_export char * |
|
452 output64(zlong val) |
|
453 { |
|
454 static char llbuf[DIGBUFSIZE]; |
|
455 convbase(llbuf, val, 0); |
|
456 return llbuf; |
|
457 } |
|
458 /**/ |
|
459 #endif /* ZSH_64_BIT_TYPE */ |