Home | History | Annotate | Download | only in csh
      1 /*
      2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1980 Regents of the University of California.
     11  * All rights reserved. The Berkeley Software License Agreement
     12  * specifies the terms and conditions for redistribution.
     13  */
     14 
     15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16 
     17 /*
     18  * This module provides with system/library function substitutes for tchar
     19  * datatype. This also includes two conversion functions between tchar and
     20  * char arrays.
     21  *
     22  * T. Kurosaka, Palo Alto, California, USA
     23  * March 1989
     24  *
     25  * Implementation Notes:
     26  *	Many functions defined here use a "char" buffer chbuf[].  In the
     27  * first attempt, there used to be only one chbuf defined as static
     28  * (private) variable and shared by these functions.  csh linked with that
     29  * version of this file misbehaved in interpreting "eval `tset ....`".
     30  * (in general, builtin function with back-quoted expression).
     31  *	This bug seemed to be caused by sharing of chbuf
     32  * by these functions simultanously (thru vfork() mechanism?).  We could not
     33  * identify which two functions interfere each other so we decided to
     34  * have each of these function its private instance of chbuf.
     35  * The size of chbuf[] might be much bigger than necessary for some functions.
     36  */
     37 #ifdef DBG
     38 #include <stdio.h>	/* For <assert.h> needs stderr defined. */
     39 #else /* !DBG */
     40 #define	NDEBUG		/* Disable assert(). */
     41 #endif /* !DBG */
     42 
     43 #include <assert.h>
     44 #include "sh.h"
     45 
     46 #ifdef MBCHAR
     47 #include <widec.h>	/* For wcsetno() */
     48 #endif
     49 
     50 #include <sys/param.h>	/* MAXPATHLEN */
     51 #include <fcntl.h>
     52 #include <unistd.h>
     53 
     54 
     55 /*
     56  * strtots(to, from): convert a char string 'from' into a tchar buffer 'to'.
     57  *	'to' is assumed to have the enough size to hold the conversion result.
     58  *	When 'to' is NOSTR(=(tchar *)0), strtots() attempts to allocate a space
     59  *	automatically using xalloc().  It is caller's responsibility to
     60  *	free the space allocated in this way, by calling xfree(ptr).
     61  *	In either case, strtots() returns the pointer to the conversion
     62  *	result (i.e. 'to', if 'to' wasn't NOSTR, or the allocated space.).
     63  *	When a conversion or allocateion failed,  NOSTR is returned.
     64  */
     65 
     66 tchar	*
     67 strtots(tchar *to, char *from)
     68 {
     69 	int	i;
     70 
     71 	if (to == NOSTR) {	/* Need to xalloc(). */
     72 		int	i;
     73 
     74 		i = mbstotcs(NOSTR, from, 0);
     75 		if (i < 0) {
     76 			return (NOSTR);
     77 		}
     78 
     79 		/* Allocate space for the resulting tchar array. */
     80 		to = (tchar *)xalloc(i * sizeof (tchar));
     81 	}
     82 	i = mbstotcs(to, from, INT_MAX);
     83 	if (i < 0) {
     84 		return (NOSTR);
     85 	}
     86 	return (to);
     87 }
     88 
     89 char	*
     90 tstostr(char *to, tchar *from)
     91 {
     92 	tchar	*ptc;
     93 	wchar_t	wc;
     94 	char	*pmb;
     95 	int	len;
     96 
     97 	if (to == (char *)NULL) {	/* Need to xalloc(). */
     98 		int	i;
     99 		int	i1;
    100 		char	junk[MB_LEN_MAX];
    101 
    102 		/* Get sum of byte counts for each char in from. */
    103 		i = 0;
    104 		ptc = from;
    105 		while (wc = (wchar_t)((*ptc++)&TRIM)) {
    106 			if ((i1 = wctomb(junk, wc)) <= 0) {
    107 				i1 = 1;
    108 			}
    109 			i += i1;
    110 		}
    111 
    112 		/* Allocate that much. */
    113 		to = (char *)xalloc(i + 1);
    114 	}
    115 
    116 	ptc = from;
    117 	pmb = to;
    118 	while (wc = (wchar_t)((*ptc++)&TRIM)) {
    119 		if ((len = wctomb(pmb, wc)) <= 0) {
    120 			*pmb = (unsigned char)wc;
    121 			len = 1;
    122 		}
    123 		pmb += len;
    124 	}
    125 	*pmb = (char)0;
    126 	return (to);
    127 }
    128 
    129 /*
    130  * mbstotcs(to, from, tosize) is similar to strtots() except that
    131  * this returns # of tchars of the resulting tchar string.
    132  * When NULL is give as the destination, no real conversion is carried out,
    133  * and the function reports how many tchar characters would be made in
    134  * the converted result including the terminating 0.
    135  *	tchar	*to;	- Destination buffer, or NULL.
    136  *	char	*from;	- Source string.
    137  *	int	tosize; - Size of to, in terms of # of tchars.
    138  */
    139 int
    140 mbstotcs(tchar *to, char *from, int tosize)
    141 {
    142 	tchar	*ptc = to;
    143 	char	*pmb = from;
    144 	wchar_t	wc;
    145 	int	chcnt = 0;
    146 	int	j;
    147 
    148 
    149 	/* Just count how many tchar would be in the result. */
    150 	if (to == (tchar *)NULL) {
    151 		while (*pmb) {
    152 			if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) {
    153 				j = 1;
    154 			}
    155 			pmb += j;
    156 			chcnt++;
    157 		}
    158 		chcnt++;	/* For terminator. */
    159 		return (chcnt);	/* # of chars including terminating zero. */
    160 	} else {	/* Do the real conversion. */
    161 		while (*pmb) {
    162 			if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) {
    163 				wc = (unsigned char)*pmb;
    164 				j = 1;
    165 			}
    166 			pmb += j;
    167 			*(ptc++) = (tchar)wc;
    168 			if (++chcnt >= tosize) {
    169 				break;
    170 			}
    171 		}
    172 		/* Terminate with zero only when space is left. */
    173 		if (chcnt < tosize) {
    174 			*ptc = (tchar)0;
    175 			++chcnt;
    176 		}
    177 		return (chcnt); /* # of chars including terminating zero. */
    178 	}
    179 }
    180 
    181 
    182 /* tchar version of STRING functions. */
    183 
    184 /*
    185  * Returns the number of
    186  * non-NULL tchar elements in tchar string argument.
    187  */
    188 int
    189 strlen_(tchar *s)
    190 {
    191 	int n;
    192 
    193 	n = 0;
    194 	while (*s++) {
    195 		n++;
    196 	}
    197 	return (n);
    198 }
    199 
    200 /*
    201  * Concatenate tchar string s2 on the end of s1.  S1's space must be large
    202  * enough.  Return s1.
    203  */
    204 tchar *
    205 strcat_(tchar *s1, tchar *s2)
    206 {
    207 	tchar *os1;
    208 
    209 	os1 = s1;
    210 	while (*s1++)
    211 		;
    212 	--s1;
    213 	while (*s1++ = *s2++)
    214 		;
    215 	return (os1);
    216 }
    217 
    218 /*
    219  * Compare tchar strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
    220  * BUGS: Comparison between two characters are done by subtracting two chars
    221  *	after converting each to an unsigned long int value.  It might not make
    222  *	a whole lot of sense to do that if the characters are in represented
    223  *	as wide characters and the two characters belong to different codesets.
    224  *	Therefore, this function should be used only to test the equallness.
    225  */
    226 int
    227 strcmp_(tchar *s1, tchar *s2)
    228 {
    229 	while (*s1 == *s2++) {
    230 		if (*s1++ == (tchar)0) {
    231 			return (0);
    232 		}
    233 	}
    234 	return (((unsigned long)*s1) - ((unsigned long)*(--s2)));
    235 }
    236 
    237 /*
    238  * This is only used in sh.glob.c for sorting purpose.
    239  */
    240 int
    241 strcoll_(tchar *s1, tchar *s2)
    242 {
    243 	char buf1[BUFSIZ];
    244 	char buf2[BUFSIZ];
    245 
    246 	tstostr(buf1, s1);
    247 	tstostr(buf2, s2);
    248 	return (strcoll(buf1, buf2));
    249 }
    250 
    251 /*
    252  * Copy tchar string s2 to s1.  s1 must be large enough.
    253  * return s1
    254  */
    255 tchar *
    256 strcpy_(tchar *s1, tchar *s2)
    257 {
    258 	tchar *os1;
    259 
    260 	os1 = s1;
    261 	while (*s1++ = *s2++)
    262 		;
    263 	return (os1);
    264 }
    265 
    266 /*
    267  * Return the ptr in sp at which the character c appears;
    268  * NULL if not found
    269  */
    270 tchar *
    271 index_(tchar *sp, tchar c)
    272 {
    273 
    274 	do {
    275 		if (*sp == c) {
    276 			return (sp);
    277 		}
    278 	} while (*sp++);
    279 	return (NULL);
    280 }
    281 
    282 /*
    283  * Return the ptr in sp at which the character c last
    284  * appears; NOSTR if not found
    285  */
    286 
    287 tchar *
    288 rindex_(tchar *sp, tchar c)
    289 {
    290 	tchar *r;
    291 
    292 	r = NOSTR;
    293 	do {
    294 		if (*sp == c) {
    295 			r = sp;
    296 		}
    297 	} while (*sp++);
    298 	return (r);
    299 }
    300 
    301 /* Additional misc functions. */
    302 
    303 /* Calculate the display width of a string.  */
    304 int
    305 tswidth(tchar *ts)
    306 {
    307 #ifdef MBCHAR
    308 	wchar_t	tc;
    309 	int	w = 0;
    310 	int	p_col;
    311 
    312 	while (tc = *ts++) {
    313 		if ((p_col = wcwidth((wchar_t)tc)) > 0)
    314 			w += p_col;
    315 	}
    316 	return (w);
    317 #else /* !MBCHAR --- one char always occupies one column. */
    318 	return (strlen_(ts));
    319 #endif
    320 }
    321 
    322 /*
    323  * Two getenv() substitute functions.  They differ in the type of arguments.
    324  * BUGS: Both returns the pointer to an allocated space where the env var's
    325  *	values is stored.  This space is freed automatically on the successive
    326  *	call of	either function.  Therefore the caller must copy the contents
    327  *	if it needs to access two env vars.  There is an arbitary limitation
    328  *	on the number of chars of a env var name.
    329  */
    330 #define	LONGEST_ENVVARNAME	256		/* Too big? */
    331 tchar *
    332 getenv_(tchar *name_)
    333 {
    334 	char	name[LONGEST_ENVVARNAME * MB_LEN_MAX];
    335 
    336 	assert(strlen_(name_) < LONGEST_ENVVARNAME);
    337 	return (getenvs_(tstostr(name, name_)));
    338 }
    339 
    340 tchar *
    341 getenvs_(char *name)
    342 {
    343 	static tchar	*pbuf = (tchar *)NULL;
    344 	char	*val;
    345 
    346 	if (pbuf) {
    347 		xfree(pbuf);
    348 		pbuf = NOSTR;
    349 	}
    350 	val = getenv(name);
    351 	if (val == (char *)NULL) {
    352 		return (NOSTR);
    353 	}
    354 	return (pbuf = strtots(NOSTR, val));
    355 }
    356 
    357 /* Followings are the system call interface for tchar strings. */
    358 
    359 /*
    360  * creat() and open() replacement.
    361  * BUGS: An unusually long file name could be dangerous.
    362  */
    363 int
    364 creat_(tchar *name_, int mode)
    365 {
    366 	int fd;
    367 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    368 
    369 	tstostr(chbuf, name_);
    370 	fd = creat((char *)chbuf, mode);
    371 	if (fd != -1) {
    372 		setfd(fd);
    373 	}
    374 	return (fd);
    375 }
    376 
    377 /*VARARGS2*/
    378 int
    379 open_(path_, flags, mode)
    380 	tchar 	*path_;
    381 	int	flags;
    382 	int	mode; /* May be omitted. */
    383 {
    384 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    385 	int fd;
    386 
    387 	tstostr(chbuf, path_);
    388 	fd = open((char *)chbuf, flags, mode);
    389 	if (fd != -1) {
    390 		setfd(fd);
    391 	}
    392 	return (fd);
    393 }
    394 
    395 /*
    396  * mkstemp replacement
    397  */
    398 int
    399 mkstemp_(tchar *name_)
    400 {
    401 	int fd;
    402 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    403 
    404 	tstostr(chbuf, name_);
    405 	fd = mkstemp((char *)chbuf);
    406 	if (fd != -1) {
    407 		setfd(fd);
    408 		strtots(name_, chbuf);
    409 	}
    410 	return (fd);
    411 }
    412 
    413 /*
    414  * read() and write() reaplacement.
    415  *	int        d;
    416  *	tchar      *buf;  - where the result be stored.  Not NULL terminated.
    417  *	int        nchreq; - # of tchars requrested.
    418  */
    419 int
    420 read_(int d, tchar *buf, int nchreq)
    421 {
    422 	unsigned char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
    423 #ifdef MBCHAR
    424 	/*
    425 	 * We would have to read more than tchar bytes
    426 	 * when there are multibyte characters in the file.
    427 	 */
    428 	int	i, j, fflags;
    429 	unsigned char	*s;	/* Byte being scanned for a multibyte char. */
    430 	/* Points to the pos where next read() to read the data into. */
    431 	unsigned char	*p;
    432 	tchar	*t;
    433 	wchar_t		wc;
    434 	int		b_len;
    435 	int		nchread = 0; /* Count how many bytes has been read. */
    436 	int		nbytread = 0; /* Total # of bytes read. */
    437 	/* # of bytes needed to complete the last char just read. */
    438 	int		delta;
    439 	unsigned char	*q;	/* q points to the first invalid byte. */
    440 	int		mb_cur_max = MB_CUR_MAX;
    441 #ifdef DBG
    442 	tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n",
    443 	    d, buf, nchreq);
    444 #endif /* DBG */
    445 	/*
    446 	 *	Step 1: We collect the exact number of bytes that make
    447 	 *	nchreq characters into chbuf.
    448 	 *	We must be careful not to read too many bytes as we
    449 	 *	cannot push back such over-read bytes.
    450 	 *	The idea we use here is that n multibyte characters are stored
    451 	 *	in no less than n but less than n*MB_CUR_MAX bytes.
    452 	 */
    453 	assert(nchreq <= BUFSIZ);
    454 	delta = 0;
    455 	p = s = chbuf;
    456 	t = buf;
    457 	while (nchread < nchreq) {
    458 		int		m;  /* # of bytes to try to read this time. */
    459 		int		k;  /* # of bytes successfully read. */
    460 
    461 retry:
    462 		/*
    463 		 * Let's say the (N+1)'th byte bN is actually the first
    464 		 * byte of a three-byte character c.
    465 		 * In that case, p, s, q look like this:
    466 		 *
    467 		 *		/-- already read--\ /-- not yet read --\
    468 		 * chbuf[]:	b0 b1 ..... bN bN+1 bN+2 bN+2 ...
    469 		 *		^		^	^
    470 		 *		|		|	|
    471 		 *		p		s	q
    472 		 *				\----------/
    473 		 *				c hasn't been completed
    474 		 *
    475 		 * Just after the next read(), p and q will be adavanced to:
    476 		 *
    477 		 *	/-- already read-----------------------\ /-- not yet -
    478 		 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2...
    479 		 *			^	^		 ^
    480 		 *			|	|		 |
    481 		 *			s	p		 q
    482 		 *			\----------/
    483 		 *			 c has been completed
    484 		 *			 but hasn't been scanned
    485 		 */
    486 		m = nchreq - nchread;
    487 		assert(p + m < chbuf + sizeof (chbuf));
    488 		k = read(d, p, m);
    489 		/*
    490 		 * when child sets O_NDELAY or O_NONBLOCK on stdin
    491 		 * and exits and we are interactive then turn the modes off
    492 		 * and retry
    493 		 */
    494 		if (k == 0) {
    495 			if ((intty && !onelflg && !cflg) &&
    496 			    ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) {
    497 				fflags &= ~O_NDELAY;
    498 				fcntl(d, F_SETFL, fflags);
    499 				goto retry;
    500 			}
    501 		} else if (k < 0) {
    502 			if (errno == EAGAIN) {
    503 				fflags = fcntl(d, F_GETFL, 0);
    504 				fflags &= ~O_NONBLOCK;
    505 				fcntl(d, F_SETFL, fflags);
    506 				goto retry;
    507 			}
    508 			return (-1);
    509 		}
    510 		nbytread += k;
    511 		q = p + k;
    512 		delta = 0;
    513 
    514 		/* Try scaning characters in s..q-1 */
    515 		while (s < q) {
    516 			/* Convert the collected bytes into tchar array. */
    517 			if (*s == 0) {
    518 				/* NUL is treated as a normal char here. */
    519 				*t++ = 0;
    520 				s++;
    521 				nchread++;
    522 				continue;
    523 			}
    524 
    525 			if ((b_len = q - s) > mb_cur_max) {
    526 				b_len = mb_cur_max;
    527 			}
    528 			if ((j = mbtowc(&wc, (char *)s, b_len)) <=  0) {
    529 				if (mb_cur_max > 1 && b_len < mb_cur_max) {
    530 					/*
    531 					 * Needs more byte to complete this char
    532 					 * In order to read() more than delta
    533 					 * bytes.
    534 					 */
    535 					break;
    536 				}
    537 				wc = (unsigned char)*s;
    538 				j = 1;
    539 			}
    540 
    541 			*t++ = wc;
    542 			nchread++;
    543 			s += j;
    544 		}
    545 
    546 		if (k < m) {
    547 			/* We've read as many bytes as possible. */
    548 			while (s < q) {
    549 				if ((b_len = q - s) > mb_cur_max) {
    550 					b_len = mb_cur_max;
    551 				}
    552 				if ((j = mbtowc(&wc, (char *)s, b_len)) <=  0) {
    553 					wc = (unsigned char)*s;
    554 					j = 1;
    555 				}
    556 				*t++ = wc;
    557 				nchread++;
    558 				s += j;
    559 			}
    560 			return (nchread);
    561 		}
    562 
    563 		p = q;
    564 	}
    565 
    566 	if (mb_cur_max == 1 || (delta = q - s) == 0) {
    567 		return (nchread);
    568 	}
    569 
    570 	/*
    571 	 * We may have (MB_CUR_MAX - 1) unread data in the buffer.
    572 	 * Here, the last converted data was an illegal character which was
    573 	 * treated as one byte character. We don't know at this point
    574 	 * whether or not the remaining data is in legal sequence.
    575 	 * We first attempt to convert the remaining data.
    576 	 */
    577 	do {
    578 		if ((j = mbtowc(&wc, (char *)s, delta)) <= 0)
    579 			break;
    580 		*t++ = wc;
    581 		nchread++;
    582 		s += j;
    583 		delta -= j;
    584 	} while (delta > 0);
    585 
    586 	if (delta == 0)
    587 		return (nchread);
    588 
    589 	/*
    590 	 * There seem to be ugly sequence in the buffer. Fill up till
    591 	 * mb_cur_max and see if we can get a right sequence.
    592 	 */
    593 	while (delta < mb_cur_max) {
    594 		assert((q + 1) < (chbuf + sizeof (chbuf)));
    595 		if (read(d, q, 1) != 1)
    596 			break;
    597 		delta++;
    598 		q++;
    599 		if (mbtowc(&wc, (char *)s, delta) > 0) {
    600 			*t = wc;
    601 			return (nchread + 1);
    602 		}
    603 	}
    604 
    605 	/*
    606 	 * no luck. we have filled MB_CUR_MAX bytes in the buffer.
    607 	 * Ideally we should return with leaving such data off and
    608 	 * put them into a local buffer for next read, but we don't
    609 	 * have such.
    610 	 * So, stop reading further, and treat them as all single
    611 	 * byte characters.
    612 	 */
    613 	while (s < q) {
    614 		b_len = q - s;
    615 		if ((j = mbtowc(&wc, (char *)s, b_len)) <=  0) {
    616 			wc = (unsigned char)*s;
    617 			j = 1;
    618 		}
    619 		*t++ = wc;
    620 		nchread++;
    621 		s += j;
    622 	}
    623 	return (nchread);
    624 
    625 #else /* !MBCHAR */
    626 	/* One byte always represents one tchar.  Easy! */
    627 	int		i;
    628 	unsigned char	*s;
    629 	tchar		*t;
    630 	int		nchread;
    631 
    632 #ifdef DBG
    633 	tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n",
    634 	    d, buf, nchreq);
    635 #endif /* DBG */
    636 	assert(nchreq <= BUFSIZ);
    637 retry:
    638 	nchread = read(d, (char *)chbuf, nchreq);
    639 	/*
    640 	 * when child sets O_NDELAY or O_NONBLOCK on stdin
    641 	 * and exits and we are interactive then turn the modes off
    642 	 * and retry
    643 	 */
    644 	if (nchread == 0) {
    645 		if ((intty && !onelflg && !cflg) &&
    646 		    ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) {
    647 			fflags &= ~O_NDELAY;
    648 			fcntl(d, F_SETFL, fflags);
    649 			goto retry;
    650 		}
    651 	} else if (nchread < 0) {
    652 		if (errno == EAGAIN) {
    653 			fflags = fcntl(d, F_GETFL, 0);
    654 			fflags &= ~O_NONBLOCK;
    655 			fcntl(d, F_SETFL, fflags);
    656 			goto retry;
    657 		}
    658 		len = 0;
    659 	} else {
    660 		for (i = 0, t = buf, s = chbuf; i < nchread; ++i) {
    661 		    *t++ = ((tchar)*s++);
    662 		}
    663 	}
    664 	return (nchread);
    665 #endif
    666 }
    667 
    668 /*
    669  * BUG: write_() returns -1 on failure, or # of BYTEs it has written.
    670  *	For consistency and symmetry, it should return the number of
    671  *	characters it has actually written, but that is technically
    672  *	difficult although not impossible.  Anyway, the return
    673  *	value of write() has never been used by the original csh,
    674  *	so this bug should be OK.
    675  */
    676 int
    677 write_(int d, tchar *buf, int nch)
    678 {
    679 	unsigned char chbuf[BUFSIZ*MB_LEN_MAX]; /* General use buffer. */
    680 #ifdef MBCHAR
    681 	tchar		*pt;
    682 	unsigned char	*pc;
    683 	wchar_t		wc;
    684 	int		i, j;
    685 
    686 #ifdef	DBG
    687 	tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n",
    688 	    d, buf, nch); /* Hope printf() doesn't call write_() itself! */
    689 #endif /* DBG */
    690 	assert(nch * MB_CUR_MAX < sizeof (chbuf));
    691 	i = nch;
    692 	pt = buf;
    693 	pc = chbuf;
    694 	while (i--) {
    695 		/*
    696 		 * Convert to tchar string.
    697 		 * NUL is treated as normal char here.
    698 		 */
    699 		wc = (wchar_t)((*pt++)&TRIM);
    700 		if (wc == (wchar_t)0) {
    701 			*pc++ = 0;
    702 		} else {
    703 			if ((j = wctomb((char *)pc, wc)) <= 0) {
    704 				*pc = (unsigned char)wc;
    705 				j = 1;
    706 			}
    707 			pc += j;
    708 		}
    709 	}
    710 	return (write(d, chbuf, pc - chbuf));
    711 #else /* !MBCHAR */
    712 	/* One byte always represents one tchar.  Easy! */
    713 	int	i;
    714 	unsigned char	*s;
    715 	tchar	*t;
    716 
    717 #ifdef	DBG
    718 	tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n",
    719 	    d, buf, nch); /* Hope printf() doesn't call write_() itself! */
    720 #endif /* DBG */
    721 	assert(nch <= sizeof (chbuf));
    722 	for (i = 0, t = buf, s = chbuf; i < nch; ++i) {
    723 	    *s++ = (char)((*t++)&0xff);
    724 	}
    725 	return (write(d, (char *)chbuf, nch));
    726 #endif
    727 }
    728 
    729 #undef chbuf
    730 
    731 #include <sys/types.h>
    732 #include <sys/stat.h>	/* satruct stat */
    733 #include <dirent.h>	/* DIR */
    734 
    735 extern DIR *Dirp;
    736 
    737 int
    738 stat_(tchar *path, struct stat *buf)
    739 {
    740 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    741 
    742 	tstostr(chbuf, path);
    743 	return (stat((char *)chbuf, buf));
    744 }
    745 
    746 int
    747 lstat_(tchar *path, struct stat *buf)
    748 {
    749 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    750 
    751 	tstostr(chbuf, path);
    752 	return (lstat((char *)chbuf, buf));
    753 }
    754 
    755 int
    756 chdir_(tchar *path)
    757 {
    758 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    759 
    760 	tstostr(chbuf, path);
    761 	return (chdir((char *)chbuf));
    762 }
    763 
    764 tchar *
    765 getwd_(tchar *path)
    766 {
    767 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    768 	int	rc;
    769 
    770 	rc = (int)getwd((char *)chbuf);
    771 	if (rc == 0) {
    772 		return (0);
    773 	} else {
    774 		return (strtots(path, chbuf));
    775 	}
    776 }
    777 
    778 int
    779 unlink_(tchar *path)
    780 {
    781 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    782 
    783 	tstostr(chbuf, path);
    784 	return (unlink((char *)chbuf));
    785 }
    786 
    787 DIR *
    788 opendir_(tchar *dirname)
    789 {
    790 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    791 
    792 	extern DIR *opendir();
    793 	DIR	*dir;
    794 
    795 	dir = opendir(tstostr(chbuf, dirname));
    796 	if (dir != NULL) {
    797 		setfd(dir->dd_fd);
    798 	}
    799 	return (Dirp = dir);
    800 }
    801 
    802 int
    803 closedir_(DIR *dirp)
    804 {
    805 	int ret;
    806 	extern int closedir();
    807 
    808 	ret = closedir(dirp);
    809 	Dirp = NULL;
    810 	return (ret);
    811 }
    812 
    813 int
    814 gethostname_(tchar *name, int namelen)
    815 {
    816 	char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
    817 
    818 	assert(namelen < BUFSIZ);
    819 	if (gethostname((char *)chbuf, sizeof (chbuf)) != 0) {
    820 		return (-1);
    821 	}
    822 	if (mbstotcs(name, chbuf, namelen) < 0) {
    823 		return (-1);
    824 	}
    825 	return (0); /* Succeeded. */
    826 }
    827 
    828 int
    829 readlink_(tchar *path, tchar *buf, int bufsiz)
    830 {
    831 	char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
    832 	char	chpath[MAXPATHLEN + 1];
    833 	int	i;
    834 
    835 	tstostr(chpath, path);
    836 	i = readlink(chpath, (char *)chbuf, sizeof (chbuf));
    837 	if (i < 0) {
    838 		return (-1);
    839 	}
    840 	chbuf[i] = (char)0;	/* readlink() doesn't put NULL. */
    841 	i = mbstotcs(buf, chbuf, bufsiz);
    842 	if (i < 0) {
    843 		return (-1);
    844 	}
    845 	return (i - 1); /* Return # of tchars EXCLUDING the terminating NULL. */
    846 }
    847 
    848 /* checks that it's a number */
    849 
    850 int
    851 chkalldigit_(tchar *str)
    852 {
    853 	char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
    854 	char *c = chbuf;
    855 
    856 	(void) tstostr(chbuf, str);
    857 
    858 	while (*c)
    859 		if (!isdigit(*(c++)))
    860 			return (-1);
    861 
    862 	return (0);
    863 }
    864 
    865 int
    866 atoi_(tchar *str)
    867 {
    868 	char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
    869 
    870 	tstostr(chbuf, str);
    871 	return (atoi((char *)chbuf));
    872 }
    873 
    874 tchar *
    875 simple(tchar *s)
    876 {
    877 	tchar *sname = s;
    878 
    879 	while (1) {
    880 		if (any('/', sname)) {
    881 			while (*sname++ != '/')
    882 				;
    883 		} else {
    884 			return (sname);
    885 		}
    886 	}
    887 }
    888