1 /*
2 * compat.c - compatibility routines for the deprived
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose. The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30 #include "zsh.mdh"
31 #include "compat.pro"
32
33 /* Return pointer to first occurrence of string t *
34 * in string s. Return NULL if not present. */
35
36 /**/
37 #ifndef HAVE_STRSTR
38
39 /**/
40 char *
strstr(const char * s,const char * t)41 strstr(const char *s, const char *t)
42 {
43 const 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
55 /**/
56 #endif
57
58
59 /**/
60 #ifndef HAVE_GETHOSTNAME
61
62 /**/
63 int
gethostname(char * name,size_t namelen)64 gethostname(char *name, size_t namelen)
65 {
66 struct utsname uts;
67
68 uname(&uts);
69 if(strlen(uts.nodename) >= namelen) {
70 errno = EINVAL;
71 return -1;
72 }
73 strcpy(name, uts.nodename);
74 return 0;
75 }
76
77 /**/
78 #endif
79
80
81 /**/
82 #ifndef HAVE_GETTIMEOFDAY
83
84 /**/
85 int
gettimeofday(struct timeval * tv,struct timezone * tz)86 gettimeofday(struct timeval *tv, struct timezone *tz)
87 {
88 tv->tv_usec = 0;
89 tv->tv_sec = (long)time((time_t) 0);
90 return 0;
91 }
92
93 /**/
94 #endif
95
96
97 /* Provide clock time with nanoseconds */
98
99 /**/
100 mod_export int
zgettime(struct timespec * ts)101 zgettime(struct timespec *ts)
102 {
103 int ret = -1;
104
105 #ifdef HAVE_CLOCK_GETTIME
106 struct timespec dts;
107 if (clock_gettime(CLOCK_REALTIME, &dts) < 0) {
108 zwarn("unable to retrieve time: %e", errno);
109 ret--;
110 } else {
111 ret++;
112 ts->tv_sec = (time_t) dts.tv_sec;
113 ts->tv_nsec = (long) dts.tv_nsec;
114 }
115 #endif
116
117 if (ret) {
118 struct timeval dtv;
119 struct timezone dtz;
120 gettimeofday(&dtv, &dtz);
121 ret++;
122 ts->tv_sec = (time_t) dtv.tv_sec;
123 ts->tv_nsec = (long) dtv.tv_usec * 1000;
124 }
125
126 return ret;
127 }
128
129
130 /* compute the difference between two calendar times */
131
132 /**/
133 #ifndef HAVE_DIFFTIME
134
135 /**/
136 double
difftime(time_t t2,time_t t1)137 difftime(time_t t2, time_t t1)
138 {
139 return ((double)t2 - (double)t1);
140 }
141
142 /**/
143 #endif
144
145
146 /**/
147 #ifndef HAVE_STRERROR
148 extern char *sys_errlist[];
149
150 /* Get error message string associated with a particular *
151 * error number, and returns a pointer to that string. *
152 * This is not a particularly robust version of strerror. */
153
154 /**/
155 char *
strerror(int errnum)156 strerror(int errnum)
157 {
158 return (sys_errlist[errnum]);
159 }
160
161 /**/
162 #endif
163
164
165 #if 0
166 /* pathconf(_PC_PATH_MAX) is not currently useful to zsh. The value *
167 * returned varies depending on a number of factors, e.g. the amount *
168 * of memory available to the operating system at a given time; thus *
169 * it can't be used for buffer allocation, or even as an indication *
170 * of whether an attempt to use or create a given pathname may fail *
171 * at any future time. *
172 * *
173 * The call is also permitted to fail if the argument path is not an *
174 * existing directory, so even to make sense of that one must search *
175 * for a valid directory somewhere in the path and adjust. Even if *
176 * it succeeds, the return value is relative to the input directory, *
177 * and therefore potentially relative to the length of the shortest *
178 * path either to that directory or to our working directory. *
179 * *
180 * Finally, see the note below for glibc; detection of pathconf() is *
181 * not by itself an indication that it works reliably. */
182
183 /* The documentation for pathconf() says something like: *
184 * The limit is returned, if one exists. If the system does *
185 * not have a limit for the requested resource, -1 is *
186 * returned, and errno is unchanged. If there is an error, *
187 * -1 is returned, and errno is set to reflect the nature of *
188 * the error. *
189 * *
190 * System calls are not permitted to set errno to 0; but we must (or *
191 * some other flag value) in order to determine that the resource is *
192 * unlimited. What use is leaving errno unchanged? Instead, define *
193 * a wrapper that resets errno to 0 and returns 0 for "the system *
194 * does not have a limit," so that -1 always means a real error. */
195
196 /**/
197 mod_export long
198 zpathmax(char *dir)
199 {
200 #ifdef HAVE_PATHCONF
201 long pathmax;
202
203 errno = 0;
204 if ((pathmax = pathconf(dir, _PC_PATH_MAX)) >= 0) {
205 /* Some versions of glibc pathconf return a hardwired value! */
206 return pathmax;
207 } else if (errno == EINVAL || errno == ENOENT || errno == ENOTDIR) {
208 /* Work backward to find a directory, until we run out of path. */
209 char *tail = strrchr(dir, '/');
210 while (tail > dir && tail[-1] == '/')
211 --tail;
212 if (tail > dir) {
213 *tail = 0;
214 pathmax = zpathmax(dir);
215 *tail = '/';
216 } else {
217 errno = 0;
218 if (tail)
219 pathmax = pathconf("/", _PC_PATH_MAX);
220 else
221 pathmax = pathconf(".", _PC_PATH_MAX);
222 }
223 if (pathmax > 0) {
224 long taillen = (tail ? strlen(tail) : (strlen(dir) + 1));
225 if (taillen < pathmax)
226 return pathmax - taillen;
227 else
228 errno = ENAMETOOLONG;
229 }
230 }
231 if (errno)
232 return -1;
233 else
234 return 0; /* pathmax should be considered unlimited */
235 #else
236 long dirlen = strlen(dir);
237
238 /* The following is wrong if dir is not an absolute path. */
239 return ((long) ((dirlen >= PATH_MAX) ?
240 ((errno = ENAMETOOLONG), -1) :
241 ((errno = 0), PATH_MAX - dirlen)));
242 #endif
243 }
244 #endif /* 0 */
245
246 /**/
247 #ifdef HAVE_SYSCONF
248 /*
249 * This is replaced by a macro from system.h if not HAVE_SYSCONF.
250 * 0 is returned by sysconf if _SC_OPEN_MAX is unavailable;
251 * -1 is returned on error
252 *
253 * Neither of these should happen, but resort to OPEN_MAX rather
254 * than return 0 or -1 just in case.
255 *
256 * We'll limit the open maximum to ZSH_INITIAL_OPEN_MAX to
257 * avoid probing ridiculous numbers of file descriptors.
258 */
259
260 /**/
261 mod_export long
zopenmax(void)262 zopenmax(void)
263 {
264 long openmax;
265
266 if ((openmax = sysconf(_SC_OPEN_MAX)) < 1) {
267 openmax = OPEN_MAX;
268 } else if (openmax > OPEN_MAX) {
269 /* On some systems, "limit descriptors unlimited" or the *
270 * equivalent will set openmax to a huge number. Unless *
271 * there actually is a file descriptor > OPEN_MAX already *
272 * open, nothing in zsh requires the true maximum, and in *
273 * fact it causes inefficiency elsewhere if we report it. *
274 * So, report the maximum of OPEN_MAX or the largest open *
275 * descriptor (is there a better way to find that?). */
276 long i, j = OPEN_MAX;
277 if (openmax > ZSH_INITIAL_OPEN_MAX)
278 openmax = ZSH_INITIAL_OPEN_MAX;
279 for (i = j; i < openmax; i += (errno != EINTR)) {
280 errno = 0;
281 if (fcntl(i, F_GETFL, 0) < 0 &&
282 (errno == EBADF || errno == EINTR))
283 continue;
284 j = i;
285 }
286 openmax = j;
287 }
288
289 return (max_zsh_fd > openmax) ? max_zsh_fd : openmax;
290 }
291
292 /**/
293 #endif
294
295 /*
296 * Rationalise the current directory, returning the string.
297 *
298 * If "d" is not NULL, it is used to store information about the
299 * directory. The returned name is also present in d->dirname and is in
300 * permanently allocated memory. The handling of this case depends on
301 * whether the fchdir() system call is available; if it is, it is assumed
302 * the caller is able to restore the current directory. On successfully
303 * identifying the directory the function returns immediately rather
304 * than ascending the hierarchy.
305 *
306 * If "d" is NULL, no assumption about the caller's behaviour is
307 * made. The returned string is in heap memory. This case is
308 * always handled by changing directory up the hierarchy.
309 *
310 * On Cygwin or other systems where USE_GETCWD is defined (at the
311 * time of writing only QNX), we skip all the above and use the
312 * getcwd() system call.
313 */
314
315 /**/
316 mod_export char *
zgetdir(struct dirsav * d)317 zgetdir(struct dirsav *d)
318 {
319 char nbuf[PATH_MAX+3];
320 char *buf;
321 int bufsiz, pos;
322 struct stat sbuf;
323 ino_t pino;
324 dev_t pdev;
325 #if !defined(__CYGWIN__) && !defined(USE_GETCWD)
326 struct dirent *de;
327 DIR *dir;
328 dev_t dev;
329 ino_t ino;
330 int len;
331 #endif
332
333 buf = zhalloc(bufsiz = PATH_MAX+1);
334 pos = bufsiz - 1;
335 buf[pos] = '\0';
336 strcpy(nbuf, "../");
337 if (stat(".", &sbuf) < 0) {
338 return NULL;
339 }
340
341 /* Record the initial inode and device */
342 pino = sbuf.st_ino;
343 pdev = sbuf.st_dev;
344 if (d)
345 d->ino = pino, d->dev = pdev;
346 #if !defined(__CYGWIN__) && !defined(USE_GETCWD)
347 #ifdef HAVE_FCHDIR
348 else
349 #endif
350 holdintr();
351
352 for (;;) {
353 /* Examine the parent of the current directory. */
354 if (stat("..", &sbuf) < 0)
355 break;
356
357 /* Inode and device of curtent directory */
358 ino = pino;
359 dev = pdev;
360 /* Inode and device of current directory's parent */
361 pino = sbuf.st_ino;
362 pdev = sbuf.st_dev;
363
364 /* If they're the same, we've reached the root directory... */
365 if (ino == pino && dev == pdev) {
366 /*
367 * ...well, probably. If this was an orphaned . after
368 * an unmount, or something such, we could be in trouble...
369 */
370 if (stat("/", &sbuf) < 0 ||
371 sbuf.st_ino != ino ||
372 sbuf.st_dev != dev) {
373 zerr("Failed to get current directory: path invalid");
374 return NULL;
375 }
376 if (!buf[pos])
377 buf[--pos] = '/';
378 if (d) {
379 #ifndef HAVE_FCHDIR
380 zchdir(buf + pos);
381 noholdintr();
382 #endif
383 return d->dirname = ztrdup(buf + pos);
384 }
385 zchdir(buf + pos);
386 noholdintr();
387 return buf + pos;
388 }
389
390 /* Search the parent for the current directory. */
391 if (!(dir = opendir("..")))
392 break;
393
394 while ((de = readdir(dir))) {
395 char *fn = de->d_name;
396 /* Ignore `.' and `..'. */
397 if (fn[0] == '.' &&
398 (fn[1] == '\0' ||
399 (fn[1] == '.' && fn[2] == '\0')))
400 continue;
401 #ifdef HAVE_STRUCT_DIRENT_D_STAT
402 if(de->d_stat.st_dev == dev && de->d_stat.st_ino == ino) {
403 /* Found the directory we're currently in */
404 strncpy(nbuf + 3, fn, PATH_MAX);
405 break;
406 }
407 #else /* !HAVE_STRUCT_DIRENT_D_STAT */
408 # ifdef HAVE_STRUCT_DIRENT_D_INO
409 if (dev != pdev || (ino_t) de->d_ino == ino)
410 # endif /* HAVE_STRUCT_DIRENT_D_INO */
411 {
412 /* Maybe found directory, need to check device & inode */
413 strncpy(nbuf + 3, fn, PATH_MAX);
414 lstat(nbuf, &sbuf);
415 if (sbuf.st_dev == dev && sbuf.st_ino == ino)
416 break;
417 }
418 #endif /* !HAVE_STRUCT_DIRENT_D_STAT */
419 }
420 closedir(dir);
421 if (!de)
422 break; /* Not found */
423 /*
424 * We get the "/" free just by copying from nbuf+2 instead
425 * of nbuf+3, which is where we copied the path component.
426 * This means buf[pos] is always a "/".
427 */
428 len = strlen(nbuf + 2);
429 pos -= len;
430 while (pos <= 1) {
431 char *newbuf = zhalloc(2*bufsiz);
432 memcpy(newbuf + bufsiz, buf, bufsiz);
433 buf = newbuf;
434 pos += bufsiz;
435 bufsiz *= 2;
436 }
437 memcpy(buf + pos, nbuf + 2, len);
438 #ifdef HAVE_FCHDIR
439 if (d)
440 return d->dirname = ztrdup(buf + pos + 1);
441 #endif
442 if (chdir(".."))
443 break;
444 }
445
446 /*
447 * Fix up the directory, if necessary.
448 * We're changing back down the hierarchy, ignore the
449 * "/" at buf[pos].
450 */
451 if (d) {
452 #ifndef HAVE_FCHDIR
453 if (buf[pos])
454 zchdir(buf + pos + 1);
455 noholdintr();
456 #endif
457 return NULL;
458 }
459
460 if (buf[pos])
461 zchdir(buf + pos + 1);
462 noholdintr();
463
464 #else /* __CYGWIN__, USE_GETCWD cases */
465
466 if (!getcwd(buf, bufsiz)) {
467 if (d) {
468 return NULL;
469 }
470 } else {
471 if (d) {
472 return d->dirname = ztrdup(buf);
473 }
474 return buf;
475 }
476 #endif
477
478 /*
479 * Something bad happened.
480 * This has been seen when inside a special directory,
481 * such as the Netapp .snapshot directory, that doesn't
482 * appear as a directory entry in the parent directory.
483 * We'll just need our best guess.
484 *
485 * We only get here from zgetcwd(); let that fall back to pwd.
486 */
487
488 return NULL;
489 }
490
491 /*
492 * Try to find the current directory.
493 * If we couldn't work it out internally, fall back to getcwd().
494 * If it fails, fall back to pwd; if zgetcwd() is being used
495 * to set pwd, pwd should be NULL and we just return ".".
496 */
497
498 /**/
499 char *
zgetcwd(void)500 zgetcwd(void)
501 {
502 char *ret = zgetdir(NULL);
503 #ifdef HAVE_GETCWD
504 if (!ret) {
505 #ifdef GETCWD_CALLS_MALLOC
506 char *cwd = getcwd(NULL, 0);
507 if (cwd) {
508 ret = dupstring(cwd);
509 free(cwd);
510 }
511 #else
512 char *cwdbuf = zalloc(PATH_MAX+1);
513 ret = getcwd(cwdbuf, PATH_MAX);
514 if (ret)
515 ret = dupstring(ret);
516 zfree(cwdbuf, PATH_MAX+1);
517 #endif /* GETCWD_CALLS_MALLOC */
518 }
519 #endif /* HAVE_GETCWD */
520 if (!ret)
521 ret = unmeta(pwd);
522 if (!ret || *ret == '\0')
523 ret = dupstring(".");
524 return ret;
525 }
526
527 /*
528 * chdir with arbitrary long pathname. Returns 0 on success, -1 on normal *
529 * failure and -2 when chdir failed and the current directory is lost.
530 *
531 * This is to be treated as if at system level, so dir is unmetafied but
532 * terminated by a NULL.
533 */
534
535 /**/
536 mod_export int
zchdir(char * dir)537 zchdir(char *dir)
538 {
539 char *s;
540 int currdir = -2;
541
542 for (;;) {
543 if (!*dir || chdir(dir) == 0) {
544 #ifdef HAVE_FCHDIR
545 if (currdir >= 0)
546 close(currdir);
547 #endif
548 return 0;
549 }
550 if ((errno != ENAMETOOLONG && errno != ENOMEM) ||
551 strlen(dir) < PATH_MAX)
552 break;
553 for (s = dir + PATH_MAX - 1; s > dir && *s != '/'; s--)
554 ;
555 if (s == dir)
556 break;
557 #ifdef HAVE_FCHDIR
558 if (currdir == -2)
559 currdir = open(".", O_RDONLY|O_NOCTTY);
560 #endif
561 *s = '\0';
562 if (chdir(dir) < 0) {
563 *s = '/';
564 break;
565 }
566 #ifndef HAVE_FCHDIR
567 currdir = -1;
568 #endif
569 *s = '/';
570 while (*++s == '/')
571 ;
572 dir = s;
573 }
574 #ifdef HAVE_FCHDIR
575 if (currdir >= 0) {
576 if (fchdir(currdir) < 0) {
577 close(currdir);
578 return -2;
579 }
580 close(currdir);
581 return -1;
582 }
583 #endif
584 return currdir == -2 ? -1 : -2;
585 }
586
587 /*
588 * How to print out a 64 bit integer. This isn't needed (1) if longs
589 * are 64 bit, since ordinary %ld will work (2) if we couldn't find a
590 * 64 bit type anyway.
591 */
592 /**/
593 #ifdef ZSH_64_BIT_TYPE
594 /**/
595 mod_export char *
output64(zlong val)596 output64(zlong val)
597 {
598 static char llbuf[DIGBUFSIZE];
599 convbase(llbuf, val, 0);
600 return llbuf;
601 }
602 /**/
603 #endif /* ZSH_64_BIT_TYPE */
604
605 /**/
606 #ifndef HAVE_STRTOUL
607
608 /*
609 * Copyright (c) 1990, 1993
610 * The Regents of the University of California. All rights reserved.
611 *
612 * Redistribution and use in source and binary forms, with or without
613 * modification, are permitted provided that the following conditions
614 * are met:
615 * 1. Redistributions of source code must retain the above copyright
616 * notice, this list of conditions and the following disclaimer.
617 * 2. Redistributions in binary form must reproduce the above copyright
618 * notice, this list of conditions and the following disclaimer in the
619 * documentation and/or other materials provided with the distribution.
620 * 3. Neither the name of the University nor the names of its contributors
621 * may be used to endorse or promote products derived from this software
622 * without specific prior written permission.
623 *
624 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
625 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
626 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
627 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
628 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
629 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
630 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
631 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
632 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
633 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
634 * SUCH DAMAGE.
635 */
636
637 /*
638 * Convert a string to an unsigned long integer.
639 *
640 * Ignores `locale' stuff. Assumes that the upper and lower case
641 * alphabets and digits are each contiguous.
642 */
643
644 /**/
645 unsigned long
strtoul(nptr,endptr,base)646 strtoul(nptr, endptr, base)
647 const char *nptr;
648 char **endptr;
649 int base;
650 {
651 const char *s;
652 unsigned long acc, cutoff;
653 int c;
654 int neg, any, cutlim;
655
656 /* endptr may be NULL */
657
658 s = nptr;
659 do {
660 c = (unsigned char) *s++;
661 } while (isspace(c));
662 if (c == '-') {
663 neg = 1;
664 c = *s++;
665 } else {
666 neg = 0;
667 if (c == '+')
668 c = *s++;
669 }
670 if ((base == 0 || base == 16) &&
671 c == '0' && (*s == 'x' || *s == 'X')) {
672 c = s[1];
673 s += 2;
674 base = 16;
675 }
676 if (base == 0)
677 base = c == '0' ? 8 : 10;
678
679 cutoff = ULONG_MAX / (unsigned long)base;
680 cutlim = (int)(ULONG_MAX % (unsigned long)base);
681 for (acc = 0, any = 0;; c = (unsigned char) *s++) {
682 if (isdigit(c))
683 c -= '0';
684 else if (isalpha(c)) {
685 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
686 } else
687 break;
688 if (c >= base)
689 break;
690 if (any < 0)
691 continue;
692 if (acc > cutoff || (acc == cutoff && c > cutlim)) {
693 any = -1;
694 acc = ULONG_MAX;
695 errno = ERANGE;
696 } else {
697 any = 1;
698 acc *= (unsigned long)base;
699 acc += c;
700 }
701 }
702 if (neg && any > 0)
703 acc = -acc;
704 if (endptr != NULL)
705 *endptr = any ? s - 1 : nptr;
706 return (acc);
707 }
708
709 /**/
710 #endif /* HAVE_STRTOUL */
711
712 /**/
713 #ifdef ENABLE_UNICODE9
714 #include "./wcwidth9.h"
715
716 /**/
717 int
u9_wcwidth(wchar_t ucs)718 u9_wcwidth(wchar_t ucs)
719 {
720 int w = wcwidth9(ucs);
721 if (w < -1)
722 return 1;
723 return w;
724 }
725
726 /**/
727 int
u9_iswprint(wint_t ucs)728 u9_iswprint(wint_t ucs)
729 {
730 if (ucs == 0)
731 return 0;
732 return wcwidth9(ucs) != -1;
733 }
734
735 /**/
736 #endif /* ENABLE_UNICODE9 */
737
738 /**/
739 #if defined(__APPLE__) && defined(BROKEN_ISPRINT)
740
741 /**/
742 int
isprint_ascii(int c)743 isprint_ascii(int c)
744 {
745 if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
746 return (c >= 0x20 && c <= 0x7e);
747 else
748 return isprint(c);
749 }
750
751 /**/
752 #endif /* __APPLE__ && BROKEN_ISPRINT */
753