summaryrefslogtreecommitdiff
path: root/usr/src/cmd/csh/sh.tchar.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/csh/sh.tchar.c')
-rw-r--r--usr/src/cmd/csh/sh.tchar.c857
1 files changed, 857 insertions, 0 deletions
diff --git a/usr/src/cmd/csh/sh.tchar.c b/usr/src/cmd/csh/sh.tchar.c
new file mode 100644
index 0000000000..9c75f0e72d
--- /dev/null
+++ b/usr/src/cmd/csh/sh.tchar.c
@@ -0,0 +1,857 @@
+/*
+ * Copyright 2000 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved. The Berkeley Software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module provides with system/library function substitutes for tchar
+ * datatype. This also includes two conversion functions between tchar and
+ * char arrays.
+ *
+ * T. Kurosaka, Palo Alto, California, USA
+ * March 1989
+ *
+ * Implementation Notes:
+ * Many functions defined here use a "char" buffer chbuf[]. In the
+ * first attempt, there used to be only one chbuf defined as static
+ * (private) variable and shared by these functions. csh linked with that
+ * version of this file misbehaved in interpreting "eval `tset ....`".
+ * (in general, builtin function with back-quoted expression).
+ * This bug seemed to be caused by sharing of chbuf
+ * by these functions simultanously (thru vfork() mechanism?). We could not
+ * identify which two functions interfere each other so we decided to
+ * have each of these function its private instance of chbuf.
+ * The size of chbuf[] might be much bigger than necessary for some functions.
+ */
+#ifdef DBG
+#include <stdio.h> /* For <assert.h> needs stderr defined. */
+#else /* !DBG */
+#define NDEBUG /* Disable assert(). */
+#endif /* !DBG */
+
+#include <assert.h>
+#include "sh.h"
+
+#ifdef MBCHAR
+#include <widec.h> /* For wcsetno() */
+#endif
+
+#include <sys/param.h> /* MAXPATHLEN */
+#include <fcntl.h>
+#include <unistd.h>
+
+
+/*
+ * strtots(to, from): convert a char string 'from' into a tchar buffer 'to'.
+ * 'to' is assumed to have the enough size to hold the conversion result.
+ * When 'to' is NOSTR(=(tchar *)0), strtots() attempts to allocate a space
+ * automatically using xalloc(). It is caller's responsibility to
+ * free the space allocated in this way, by calling XFREE(ptr).
+ * In either case, strtots() returns the pointer to the conversion
+ * result (i.e. 'to', if 'to' wasn't NOSTR, or the allocated space.).
+ * When a conversion or allocateion failed, NOSTR is returned.
+ */
+
+tchar *
+strtots(tchar *to, char *from)
+{
+ int i;
+
+ if (to == NOSTR) { /* Need to xalloc(). */
+ int i;
+
+ i = mbstotcs(NOSTR, from, 0);
+ if (i < 0) {
+ return (NOSTR);
+ }
+
+ /* Allocate space for the resulting tchar array. */
+ to = (tchar *)xalloc(i * sizeof (tchar));
+ }
+ i = mbstotcs(to, from, INT_MAX);
+ if (i < 0) {
+ return (NOSTR);
+ }
+ return (to);
+}
+
+char *
+tstostr(char *to, tchar *from)
+{
+ tchar *ptc;
+ wchar_t wc;
+ char *pmb;
+ int len;
+
+ if (to == (char *)NULL) { /* Need to xalloc(). */
+ int i;
+ int i1;
+ char junk[MB_LEN_MAX];
+
+ /* Get sum of byte counts for each char in from. */
+ i = 0;
+ ptc = from;
+ while (wc = (wchar_t)((*ptc++)&TRIM)) {
+ if ((i1 = wctomb(junk, wc)) <= 0) {
+ i1 = 1;
+ }
+ i += i1;
+ }
+
+ /* Allocate that much. */
+ to = (char *)xalloc(i + 1);
+ }
+
+ ptc = from;
+ pmb = to;
+ while (wc = (wchar_t)((*ptc++)&TRIM)) {
+ if ((len = wctomb(pmb, wc)) <= 0) {
+ *pmb = (unsigned char)wc;
+ len = 1;
+ }
+ pmb += len;
+ }
+ *pmb = (char)0;
+ return (to);
+}
+
+/*
+ * mbstotcs(to, from, tosize) is similar to strtots() except that
+ * this returns # of tchars of the resulting tchar string.
+ * When NULL is give as the destination, no real conversion is carried out,
+ * and the function reports how many tchar characters would be made in
+ * the converted result including the terminating 0.
+ * tchar *to; - Destination buffer, or NULL.
+ * char *from; - Source string.
+ * int tosize; - Size of to, in terms of # of tchars.
+ */
+int
+mbstotcs(tchar *to, char *from, int tosize)
+{
+ tchar *ptc = to;
+ char *pmb = from;
+ wchar_t wc;
+ int chcnt = 0;
+ int j;
+
+
+ /* Just count how many tchar would be in the result. */
+ if (to == (tchar *)NULL) {
+ while (*pmb) {
+ if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) {
+ j = 1;
+ }
+ pmb += j;
+ chcnt++;
+ }
+ chcnt++; /* For terminator. */
+ return (chcnt); /* # of chars including terminating zero. */
+ } else { /* Do the real conversion. */
+ while (*pmb) {
+ if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) {
+ wc = (unsigned char)*pmb;
+ j = 1;
+ }
+ pmb += j;
+ *(ptc++) = (tchar)wc;
+ if (++chcnt >= tosize) {
+ break;
+ }
+ }
+ /* Terminate with zero only when space is left. */
+ if (chcnt < tosize) {
+ *ptc = (tchar)0;
+ ++chcnt;
+ }
+ return (chcnt); /* # of chars including terminating zero. */
+ }
+}
+
+
+/* tchar version of STRING functions. */
+
+/*
+ * Returns the number of
+ * non-NULL tchar elements in tchar string argument.
+ */
+int
+strlen_(tchar *s)
+{
+ int n;
+
+ n = 0;
+ while (*s++) {
+ n++;
+ }
+ return (n);
+}
+
+/*
+ * Concatenate tchar string s2 on the end of s1. S1's space must be large
+ * enough. Return s1.
+ */
+tchar *
+strcat_(tchar *s1, tchar *s2)
+{
+ tchar *os1;
+
+ os1 = s1;
+ while (*s1++)
+ ;
+ --s1;
+ while (*s1++ = *s2++)
+ ;
+ return (os1);
+}
+
+/*
+ * Compare tchar strings: s1>s2: >0 s1==s2: 0 s1<s2: <0
+ * BUGS: Comparison between two characters are done by subtracting two chars
+ * after converting each to an unsigned long int value. It might not make
+ * a whole lot of sense to do that if the characters are in represented
+ * as wide characters and the two characters belong to different codesets.
+ * Therefore, this function should be used only to test the equallness.
+ */
+int
+strcmp_(tchar *s1, tchar *s2)
+{
+ while (*s1 == *s2++) {
+ if (*s1++ == (tchar)0) {
+ return (0);
+ }
+ }
+ return (((unsigned long)*s1) - ((unsigned long)*(--s2)));
+}
+
+/*
+ * This is only used in sh.glob.c for sorting purpose.
+ */
+int
+strcoll_(tchar *s1, tchar *s2)
+{
+ char buf1[BUFSIZ];
+ char buf2[BUFSIZ];
+
+ tstostr(buf1, s1);
+ tstostr(buf2, s2);
+ return (strcoll(buf1, buf2));
+}
+
+/*
+ * Copy tchar string s2 to s1. s1 must be large enough.
+ * return s1
+ */
+tchar *
+strcpy_(tchar *s1, tchar *s2)
+{
+ tchar *os1;
+
+ os1 = s1;
+ while (*s1++ = *s2++)
+ ;
+ return (os1);
+}
+
+/*
+ * Return the ptr in sp at which the character c appears;
+ * NULL if not found
+ */
+tchar *
+index_(tchar *sp, tchar c)
+{
+
+ do {
+ if (*sp == c) {
+ return (sp);
+ }
+ } while (*sp++);
+ return (NULL);
+}
+
+/*
+ * Return the ptr in sp at which the character c last
+ * appears; NOSTR if not found
+ */
+
+tchar *
+rindex_(tchar *sp, tchar c)
+{
+ tchar *r;
+
+ r = NOSTR;
+ do {
+ if (*sp == c) {
+ r = sp;
+ }
+ } while (*sp++);
+ return (r);
+}
+
+/* Additional misc functions. */
+
+/* Calculate the display width of a string. */
+tswidth(tchar *ts)
+{
+#ifdef MBCHAR
+ wchar_t tc;
+ int w = 0;
+ int p_col;
+
+ while (tc = *ts++) {
+ if ((p_col = wcwidth((wchar_t)tc)) > 0)
+ w += p_col;
+ }
+ return (w);
+#else /* !MBCHAR --- one char always occupies one column. */
+ return (strlen_(ts));
+#endif
+}
+
+/*
+ * Two getenv() substitute functions. They differ in the type of arguments.
+ * BUGS: Both returns the pointer to an allocated space where the env var's
+ * values is stored. This space is freed automatically on the successive
+ * call of either function. Therefore the caller must copy the contents
+ * if it needs to access two env vars. There is an arbitary limitation
+ * on the number of chars of a env var name.
+ */
+#define LONGEST_ENVVARNAME 256 /* Too big? */
+tchar *
+getenv_(tchar *name_)
+{
+ char name[LONGEST_ENVVARNAME * MB_LEN_MAX];
+
+ assert(strlen_(name_) < LONGEST_ENVVARNAME);
+ return (getenvs_(tstostr(name, name_)));
+}
+
+tchar *
+getenvs_(char *name)
+{
+ static tchar *pbuf = (tchar *)NULL;
+ char *val;
+
+ if (pbuf) {
+ XFREE((void *)pbuf);
+ pbuf = NOSTR;
+ }
+ val = getenv(name);
+ if (val == (char *)NULL) {
+ return (NOSTR);
+ }
+ return (pbuf = strtots(NOSTR, val));
+}
+
+/* Followings are the system call interface for tchar strings. */
+
+/*
+ * creat() and open() replacement.
+ * BUGS: An unusually long file name could be dangerous.
+ */
+int
+creat_(tchar *name_, int mode)
+{
+ int fd;
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, name_);
+ fd = creat((char *)chbuf, mode);
+ if (fd != -1) {
+ setfd(fd);
+ }
+ return (fd);
+}
+
+/*VARARGS2*/
+int
+open_(path_, flags, mode)
+ tchar *path_;
+ int flags;
+ int mode; /* May be omitted. */
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+ int fd;
+
+ tstostr(chbuf, path_);
+ fd = open((char *)chbuf, flags, mode);
+ if (fd != -1) {
+ setfd(fd);
+ }
+ return (fd);
+}
+
+/*
+ * mkstemp replacement
+ */
+int
+mkstemp_(tchar *name_)
+{
+ int fd;
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, name_);
+ fd = mkstemp((char *)chbuf);
+ if (fd != -1) {
+ setfd(fd);
+ strtots(name_, chbuf);
+ }
+ return (fd);
+}
+
+/*
+ * read() and write() reaplacement.
+ * int d;
+ * tchar *buf; - where the result be stored. Not NULL terminated.
+ * int nchreq; - # of tchars requrested.
+ */
+int
+read_(int d, tchar *buf, int nchreq)
+{
+ unsigned char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
+#ifdef MBCHAR
+ /*
+ * We would have to read more than tchar bytes
+ * when there are multibyte characters in the file.
+ */
+ int i, j, fflags;
+ unsigned char *s; /* Byte being scanned for a multibyte char. */
+ /* Points to the pos where next read() to read the data into. */
+ unsigned char *p;
+ tchar *t;
+ wchar_t wc;
+ int b_len;
+ int nchread = 0; /* Count how many bytes has been read. */
+ int nbytread = 0; /* Total # of bytes read. */
+ /* # of bytes needed to complete the last char just read. */
+ int delta;
+ unsigned char *q; /* q points to the first invalid byte. */
+#ifdef DBG
+ tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n",
+ d, buf, nchreq);
+#endif /* DBG */
+ /*
+ * Step 1: We collect the exact number of bytes that make
+ * nchreq characters into chbuf.
+ * We must be careful not to read too many bytes as we
+ * cannot push back such over-read bytes.
+ * The idea we use here is that n multibyte characters are stored
+ * in no less than n but less than n*MB_CUR_MAX bytes.
+ */
+ assert(nchreq <= BUFSIZ);
+ delta = 0;
+ p = s = chbuf;
+ t = buf;
+ while (nchread < nchreq) {
+ int m; /* # of bytes to try to read this time. */
+ int k; /* # of bytes successfully read. */
+
+retry:
+ /*
+ * Let's say the (N+1)'th byte bN is actually the first
+ * byte of a three-byte character c.
+ * In that case, p, s, q look like this:
+ *
+ * /-- already read--\ /-- not yet read --\
+ * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ...
+ * ^ ^ ^
+ * | | |
+ * p s q
+ * \----------/
+ * c hasn't been completed
+ *
+ * Just after the next read(), p and q will be adavanced to:
+ *
+ * /-- already read-----------------------\ /-- not yet -
+ * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2...
+ * ^ ^ ^
+ * | | |
+ * s p q
+ * \----------/
+ * c has been completed
+ * but hasn't been scanned
+ */
+ m = nchreq - nchread;
+ assert(p + m < chbuf + sizeof (chbuf));
+ k = read(d, p, m);
+ /*
+ * when child sets O_NDELAY or O_NONBLOCK on stdin
+ * and exits and we are interactive then turn the modes off
+ * and retry
+ */
+ if (k == 0) {
+ if ((intty && !onelflg && !cflg) &&
+ ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) {
+ fflags &= ~O_NDELAY;
+ fcntl(d, F_SETFL, fflags);
+ goto retry;
+ }
+ } else if (k < 0) {
+ if (errno == EAGAIN) {
+ fflags = fcntl(d, F_GETFL, 0);
+ fflags &= ~O_NONBLOCK;
+ fcntl(d, F_SETFL, fflags);
+ goto retry;
+ }
+ return (-1);
+ }
+ nbytread += k;
+ q = p + k;
+ delta = 0;
+
+ /* Try scaning characters in s..q-1 */
+ while (s < q) {
+ /* Convert the collected bytes into tchar array. */
+ if (*s == 0) {
+ /* NUL is treated as a normal char here. */
+ *t++ = 0;
+ s++;
+ nchread++;
+ continue;
+ }
+
+ if ((b_len = q - s) > (int)MB_CUR_MAX) {
+ b_len = MB_CUR_MAX;
+ }
+ if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) {
+ if (b_len < (unsigned int)MB_CUR_MAX) {
+ /*
+ * Needs more byte to complete this char
+ * In order to read() more than delta
+ * bytes.
+ */
+ break;
+ }
+
+ wc = (unsigned char)*s;
+ j = 1;
+ }
+
+ *t++ = wc;
+ nchread++;
+ s += j;
+ }
+
+ if (k < m) {
+ /* We've read as many bytes as possible. */
+ while (s < q) {
+ if ((b_len = q - s) > (int)MB_CUR_MAX) {
+ b_len = MB_CUR_MAX;
+ }
+ if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) {
+ wc = (unsigned char)*s;
+ j = 1;
+ }
+ *t++ = wc;
+ nchread++;
+ s += j;
+ }
+ return (nchread);
+ }
+
+ p = q;
+ }
+
+ if ((delta = q - s) == 0) {
+ return (nchread);
+ }
+
+ if (*(s + delta - 1) == '\n') {
+ while (s < q) {
+ if ((b_len = q - s) > (int)MB_CUR_MAX) {
+ b_len = MB_CUR_MAX;
+ }
+ if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) {
+ wc = (unsigned char)*s;
+ j = 1;
+ }
+ *t++ = wc;
+ nchread++;
+ s += j;
+ }
+ return (nchread);
+ }
+
+ for (; delta < (int)MB_CUR_MAX; delta++, q++) {
+ assert((q + 1) < (chbuf + sizeof (chbuf)));
+ if (read(d, q, 1) != 1) {
+ break;
+ }
+ if (*q == '\n') {
+ break;
+ }
+ if (mbtowc(&wc, (char *)s, delta) > 0) {
+ *t = wc;
+ return (nchread + 1);
+ }
+ }
+
+ while (s < q) {
+ if ((b_len = q - s) > (int)MB_CUR_MAX) {
+ b_len = MB_CUR_MAX;
+ }
+ if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) {
+ wc = (unsigned char)*s;
+ j = 1;
+ }
+ *t++ = wc;
+ nchread++;
+ s += j;
+ }
+ return (nchread);
+#else /* !MBCHAR */
+ /* One byte always represents one tchar. Easy! */
+ int i;
+ unsigned char *s;
+ tchar *t;
+ int nchread;
+
+#ifdef DBG
+ tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n",
+ d, buf, nchreq);
+#endif /* DBG */
+ assert(nchreq <= BUFSIZ);
+retry:
+ nchread = read(d, (char *)chbuf, nchreq);
+ /*
+ * when child sets O_NDELAY or O_NONBLOCK on stdin
+ * and exits and we are interactive then turn the modes off
+ * and retry
+ */
+ if (nchread == 0) {
+ if ((intty && !onelflg && !cflg) &&
+ ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) {
+ fflags &= ~O_NDELAY;
+ fcntl(d, F_SETFL, fflags);
+ goto retry;
+ }
+ } else if (nchread < 0) {
+ if (errno == EAGAIN) {
+ fflags = fcntl(d, F_GETFL, 0);
+ fflags &= ~O_NONBLOCK;
+ fcntl(d, F_SETFL, fflags);
+ goto retry;
+ }
+ len = 0;
+ } else {
+ for (i = 0, t = buf, s = chbuf; i < nchread; ++i) {
+ *t++ = ((tchar)*s++);
+ }
+ }
+ return (nchread);
+#endif
+}
+
+/*
+ * BUG: write_() returns -1 on failure, or # of BYTEs it has written.
+ * For consistency and symmetry, it should return the number of
+ * characters it has actually written, but that is technically
+ * difficult although not impossible. Anyway, the return
+ * value of write() has never been used by the original csh,
+ * so this bug should be OK.
+ */
+int
+write_(int d, tchar *buf, int nch)
+{
+ unsigned char chbuf[BUFSIZ*MB_LEN_MAX]; /* General use buffer. */
+#ifdef MBCHAR
+ tchar *pt;
+ unsigned char *pc;
+ wchar_t wc;
+ int i, j;
+
+#ifdef DBG
+ tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n",
+ d, buf, nch); /* Hope printf() doesn't call write_() itself! */
+#endif /* DBG */
+ assert(nch * MB_CUR_MAX < sizeof (chbuf));
+ i = nch;
+ pt = buf;
+ pc = chbuf;
+ while (i--) {
+ /*
+ * Convert to tchar string.
+ * NUL is treated as normal char here.
+ */
+ wc = (wchar_t)((*pt++)&TRIM);
+ if (wc == (wchar_t)0) {
+ *pc++ = 0;
+ } else {
+ if ((j = wctomb((char *)pc, wc)) <= 0) {
+ *pc = (unsigned char)wc;
+ j = 1;
+ }
+ pc += j;
+ }
+ }
+ return (write(d, chbuf, pc - chbuf));
+#else /* !MBCHAR */
+ /* One byte always represents one tchar. Easy! */
+ int i;
+ unsigned char *s;
+ tchar *t;
+
+#ifdef DBG
+ tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n",
+ d, buf, nch); /* Hope printf() doesn't call write_() itself! */
+#endif /* DBG */
+ assert(nch <= sizeof (chbuf));
+ for (i = 0, t = buf, s = chbuf; i < nch; ++i) {
+ *s++ = (char)((*t++)&0xff);
+ }
+ return (write(d, (char *)chbuf, nch));
+#endif
+}
+
+#undef chbuf
+
+#include <sys/types.h>
+#include <sys/stat.h> /* satruct stat */
+#include <dirent.h> /* DIR */
+
+extern DIR *Dirp;
+
+int
+stat_(tchar *path, struct stat *buf)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, path);
+ return (stat((char *)chbuf, buf));
+}
+
+int
+lstat_(tchar *path, struct stat *buf)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, path);
+ return (lstat((char *)chbuf, buf));
+}
+
+int
+chdir_(tchar *path)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, path);
+ return (chdir((char *)chbuf));
+}
+
+tchar *
+getwd_(tchar *path)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+ int rc;
+
+ rc = (int)getwd((char *)chbuf);
+ if (rc == 0) {
+ return (0);
+ } else {
+ return (strtots(path, chbuf));
+ }
+}
+
+int
+unlink_(tchar *path)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, path);
+ return (unlink((char *)chbuf));
+}
+
+DIR *
+opendir_(tchar *dirname)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+
+ extern DIR *opendir();
+ DIR *dir;
+
+ dir = opendir(tstostr(chbuf, dirname));
+ if (dir != NULL) {
+ setfd(dir->dd_fd);
+ }
+ return (Dirp = dir);
+}
+
+int
+closedir_(DIR *dirp)
+{
+ int ret;
+ extern int closedir();
+
+ ret = closedir(dirp);
+ Dirp = NULL;
+ return (ret);
+}
+
+int
+gethostname_(tchar *name, int namelen)
+{
+ char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
+
+ assert(namelen < BUFSIZ);
+ if (gethostname((char *)chbuf, sizeof (chbuf)) != 0) {
+ return (-1);
+ }
+ if (mbstotcs(name, chbuf, namelen) < 0) {
+ return (-1);
+ }
+ return (0); /* Succeeded. */
+}
+
+int
+readlink_(tchar *path, tchar *buf, int bufsiz)
+{
+ char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */
+ char chpath[MAXPATHLEN + 1];
+ int i;
+
+ tstostr(chpath, path);
+ i = readlink(chpath, (char *)chbuf, sizeof (chbuf));
+ if (i < 0) {
+ return (-1);
+ }
+ chbuf[i] = (char)0; /* readlink() doesn't put NULL. */
+ i = mbstotcs(buf, chbuf, bufsiz);
+ if (i < 0) {
+ return (-1);
+ }
+ return (i - 1); /* Return # of tchars EXCLUDING the terminating NULL. */
+}
+
+int
+atoi_(tchar *str)
+{
+ char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */
+
+ tstostr(chbuf, str);
+ return (atoi((char *)chbuf));
+}
+
+tchar *
+simple(tchar *s)
+{
+ register tchar *sname = s;
+
+ while (1) {
+ if (any('/', sname)) {
+ while (*sname++ != '/')
+ ;
+ } else {
+ return (sname);
+ }
+ }
+}