2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
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.
15 #pragma ident "%Z%%M% %I% %E% SMI"
18 * This module provides with system/library function substitutes for tchar
19 * datatype. This also includes two conversion functions between tchar and
22 * T. Kurosaka, Palo Alto, California, USA
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.
38 #include <stdio.h> /* For <assert.h> needs stderr defined. */
40 #define NDEBUG /* Disable assert(). */
47 #include <widec.h> /* For wcsetno() */
50 #include <sys/param.h> /* MAXPATHLEN */
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.
67 strtots(tchar
*to
, char *from
)
71 if (to
== NOSTR
) { /* Need to xalloc(). */
74 i
= mbstotcs(NOSTR
, from
, 0);
79 /* Allocate space for the resulting tchar array. */
80 to
= (tchar
*)xalloc(i
* sizeof (tchar
));
82 i
= mbstotcs(to
, from
, INT_MAX
);
90 tstostr(char *to
, tchar
*from
)
97 if (to
== NULL
) { /* Need to xalloc(). */
100 char junk
[MB_LEN_MAX
];
102 /* Get sum of byte counts for each char in from. */
105 while (wc
= (wchar_t)((*ptc
++)&TRIM
)) {
106 if ((i1
= wctomb(junk
, wc
)) <= 0) {
112 /* Allocate that much. */
113 to
= (char *)xalloc(i
+ 1);
118 while (wc
= (wchar_t)((*ptc
++)&TRIM
)) {
119 if ((len
= wctomb(pmb
, wc
)) <= 0) {
120 *pmb
= (unsigned char)wc
;
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.
140 mbstotcs(tchar
*to
, char *from
, int tosize
)
149 /* Just count how many tchar would be in the result. */
150 if (to
== (tchar
*)NULL
) {
152 if ((j
= mbtowc(&wc
, pmb
, MB_CUR_MAX
)) <= 0) {
158 chcnt
++; /* For terminator. */
159 return (chcnt
); /* # of chars including terminating zero. */
160 } else { /* Do the real conversion. */
162 if ((j
= mbtowc(&wc
, pmb
, MB_CUR_MAX
)) <= 0) {
163 wc
= (unsigned char)*pmb
;
167 *(ptc
++) = (tchar
)wc
;
168 if (++chcnt
>= tosize
) {
172 /* Terminate with zero only when space is left. */
173 if (chcnt
< tosize
) {
177 return (chcnt
); /* # of chars including terminating zero. */
182 /* tchar version of STRING functions. */
185 * Returns the number of
186 * non-NULL tchar elements in tchar string argument.
201 * Concatenate tchar string s2 on the end of s1. S1's space must be large
205 strcat_(tchar
*s1
, tchar
*s2
)
213 while (*s1
++ = *s2
++)
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.
227 strcmp_(tchar
*s1
, tchar
*s2
)
229 while (*s1
== *s2
++) {
230 if (*s1
++ == (tchar
)0) {
234 return (((unsigned long)*s1
) - ((unsigned long)*(--s2
)));
238 * This is only used in sh.glob.c for sorting purpose.
241 strcoll_(tchar
*s1
, tchar
*s2
)
248 return (strcoll(buf1
, buf2
));
252 * Copy tchar string s2 to s1. s1 must be large enough.
256 strcpy_(tchar
*s1
, tchar
*s2
)
261 while (*s1
++ = *s2
++)
267 * Return the ptr in sp at which the character c appears;
271 index_(tchar
*sp
, tchar c
)
283 * Return the ptr in sp at which the character c last
284 * appears; NOSTR if not found
288 rindex_(tchar
*sp
, tchar c
)
301 /* Additional misc functions. */
303 /* Calculate the display width of a string. */
313 if ((p_col
= wcwidth((wchar_t)tc
)) > 0)
317 #else /* !MBCHAR --- one char always occupies one column. */
318 return (strlen_(ts
));
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.
330 #define LONGEST_ENVVARNAME 256 /* Too big? */
332 getenv_(tchar
*name_
)
334 char name
[LONGEST_ENVVARNAME
* MB_LEN_MAX
];
336 assert(strlen_(name_
) < LONGEST_ENVVARNAME
);
337 return (getenvs_(tstostr(name
, name_
)));
343 static tchar
*pbuf
= (tchar
*)NULL
;
354 return (pbuf
= strtots(NOSTR
, val
));
357 /* Followings are the system call interface for tchar strings. */
360 * creat() and open() replacement.
361 * BUGS: An unusually long file name could be dangerous.
364 creat_(tchar
*name_
, int mode
)
367 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
369 tstostr(chbuf
, name_
);
370 fd
= creat((char *)chbuf
, mode
);
379 open_(path_
, flags
, mode
)
382 int mode
; /* May be omitted. */
384 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
387 tstostr(chbuf
, path_
);
388 fd
= open((char *)chbuf
, flags
, mode
);
396 * mkstemp replacement
399 mkstemp_(tchar
*name_
)
402 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
404 tstostr(chbuf
, name_
);
405 fd
= mkstemp((char *)chbuf
);
408 strtots(name_
, chbuf
);
414 * read() and write() reaplacement.
416 * tchar *buf; - where the result be stored. Not NULL terminated.
417 * int nchreq; - # of tchars requrested.
420 read_(int d
, tchar
*buf
, int nchreq
)
422 unsigned char chbuf
[BUFSIZ
* MB_LEN_MAX
]; /* General use buffer. */
425 * We would have to read more than tchar bytes
426 * when there are multibyte characters in the file.
429 unsigned char *s
; /* Byte being scanned for a multibyte char. */
430 /* Points to the pos where next read() to read the data into. */
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. */
439 unsigned char *q
; /* q points to the first invalid byte. */
440 int mb_cur_max
= MB_CUR_MAX
;
442 tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n",
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.
453 assert(nchreq
<= BUFSIZ
);
457 while (nchread
< nchreq
) {
458 int m
; /* # of bytes to try to read this time. */
459 int k
; /* # of bytes successfully read. */
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:
467 * /-- already read--\ /-- not yet read --\
468 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ...
473 * c hasn't been completed
475 * Just after the next read(), p and q will be adavanced to:
477 * /-- already read-----------------------\ /-- not yet -
478 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2...
483 * c has been completed
484 * but hasn't been scanned
486 m
= nchreq
- nchread
;
487 assert(p
+ m
< chbuf
+ sizeof (chbuf
));
490 * when child sets O_NDELAY or O_NONBLOCK on stdin
491 * and exits and we are interactive then turn the modes off
495 if ((intty
&& !onelflg
&& !cflg
) &&
496 ((fflags
= fcntl(d
, F_GETFL
, 0)) & O_NDELAY
)) {
498 fcntl(d
, F_SETFL
, fflags
);
502 if (errno
== EAGAIN
) {
503 fflags
= fcntl(d
, F_GETFL
, 0);
504 fflags
&= ~O_NONBLOCK
;
505 fcntl(d
, F_SETFL
, fflags
);
514 /* Try scaning characters in s..q-1 */
516 /* Convert the collected bytes into tchar array. */
518 /* NUL is treated as a normal char here. */
525 if ((b_len
= q
- s
) > mb_cur_max
) {
528 if ((j
= mbtowc(&wc
, (char *)s
, b_len
)) <= 0) {
529 if (mb_cur_max
> 1 && b_len
< mb_cur_max
) {
531 * Needs more byte to complete this char
532 * In order to read() more than delta
537 wc
= (unsigned char)*s
;
547 /* We've read as many bytes as possible. */
549 if ((b_len
= q
- s
) > mb_cur_max
) {
552 if ((j
= mbtowc(&wc
, (char *)s
, b_len
)) <= 0) {
553 wc
= (unsigned char)*s
;
566 if (mb_cur_max
== 1 || (delta
= q
- s
) == 0) {
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.
578 if ((j
= mbtowc(&wc
, (char *)s
, delta
)) <= 0)
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.
593 while (delta
< mb_cur_max
) {
594 assert((q
+ 1) < (chbuf
+ sizeof (chbuf
)));
595 if (read(d
, q
, 1) != 1)
599 if (mbtowc(&wc
, (char *)s
, delta
) > 0) {
601 return (nchread
+ 1);
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
610 * So, stop reading further, and treat them as all single
615 if ((j
= mbtowc(&wc
, (char *)s
, b_len
)) <= 0) {
616 wc
= (unsigned char)*s
;
626 /* One byte always represents one tchar. Easy! */
633 tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n",
636 assert(nchreq
<= BUFSIZ
);
638 nchread
= read(d
, (char *)chbuf
, nchreq
);
640 * when child sets O_NDELAY or O_NONBLOCK on stdin
641 * and exits and we are interactive then turn the modes off
645 if ((intty
&& !onelflg
&& !cflg
) &&
646 ((fflags
= fcntl(d
, F_GETFL
, 0)) & O_NDELAY
)) {
648 fcntl(d
, F_SETFL
, fflags
);
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
);
660 for (i
= 0, t
= buf
, s
= chbuf
; i
< nchread
; ++i
) {
661 *t
++ = ((tchar
)*s
++);
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.
677 write_(int d
, tchar
*buf
, int nch
)
679 unsigned char chbuf
[BUFSIZ
*MB_LEN_MAX
]; /* General use buffer. */
687 tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n",
688 d
, buf
, nch
); /* Hope printf() doesn't call write_() itself! */
690 assert(nch
* MB_CUR_MAX
< sizeof (chbuf
));
696 * Convert to tchar string.
697 * NUL is treated as normal char here.
699 wc
= (wchar_t)((*pt
++)&TRIM
);
700 if (wc
== (wchar_t)0) {
703 if ((j
= wctomb((char *)pc
, wc
)) <= 0) {
704 *pc
= (unsigned char)wc
;
710 return (write(d
, chbuf
, pc
- chbuf
));
712 /* One byte always represents one tchar. Easy! */
718 tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n",
719 d
, buf
, nch
); /* Hope printf() doesn't call write_() itself! */
721 assert(nch
<= sizeof (chbuf
));
722 for (i
= 0, t
= buf
, s
= chbuf
; i
< nch
; ++i
) {
723 *s
++ = (char)((*t
++)&0xff);
725 return (write(d
, (char *)chbuf
, nch
));
731 #include <sys/types.h>
732 #include <sys/stat.h> /* satruct stat */
733 #include <dirent.h> /* DIR */
738 stat_(tchar
*path
, struct stat
*buf
)
740 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
742 tstostr(chbuf
, path
);
743 return (stat((char *)chbuf
, buf
));
747 lstat_(tchar
*path
, struct stat
*buf
)
749 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
751 tstostr(chbuf
, path
);
752 return (lstat((char *)chbuf
, buf
));
758 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
760 tstostr(chbuf
, path
);
761 return (chdir((char *)chbuf
));
767 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
770 rc
= (int)getwd((char *)chbuf
);
774 return (strtots(path
, chbuf
));
781 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
783 tstostr(chbuf
, path
);
784 return (unlink((char *)chbuf
));
788 opendir_(tchar
*dirname
)
790 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
792 extern DIR *opendir();
795 dir
= opendir(tstostr(chbuf
, dirname
));
806 extern int closedir();
808 ret
= closedir(dirp
);
814 gethostname_(tchar
*name
, int namelen
)
816 char chbuf
[BUFSIZ
* MB_LEN_MAX
]; /* General use buffer. */
818 assert(namelen
< BUFSIZ
);
819 if (gethostname((char *)chbuf
, sizeof (chbuf
)) != 0) {
822 if (mbstotcs(name
, chbuf
, namelen
) < 0) {
825 return (0); /* Succeeded. */
829 readlink_(tchar
*path
, tchar
*buf
, int bufsiz
)
831 char chbuf
[MAXPATHLEN
* MB_LEN_MAX
]; /* General use buffer. */
832 char chpath
[MAXPATHLEN
+ 1];
835 tstostr(chpath
, path
);
836 i
= readlink(chpath
, (char *)chbuf
, sizeof (chbuf
));
840 chbuf
[i
] = '\0'; /* readlink() doesn't put NULL. */
841 i
= mbstotcs(buf
, chbuf
, bufsiz
);
845 return (i
- 1); /* Return # of tchars EXCLUDING the terminating NULL. */
848 /* checks that it's a number */
851 chkalldigit_(tchar
*str
)
853 char chbuf
[BUFSIZ
* MB_LEN_MAX
]; /* General use buffer. */
856 (void) tstostr(chbuf
, str
);
859 if (!isdigit(*(c
++)))
868 char chbuf
[BUFSIZ
* MB_LEN_MAX
]; /* General use buffer. */
871 return (atoi((char *)chbuf
));
880 if (any('/', sname
)) {
881 while (*sname
++ != '/')