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