xref: /illumos-gate/usr/src/cmd/csh/sh.tchar.c (revision 598f4ceed9327d2d6c2325dd67cae3aa06f7fea6)
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