1 /* 2 * Copyright 2005 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((void *)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 #ifdef DBG 441 tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", 442 d, buf, nchreq); 443 #endif /* DBG */ 444 /* 445 * Step 1: We collect the exact number of bytes that make 446 * nchreq characters into chbuf. 447 * We must be careful not to read too many bytes as we 448 * cannot push back such over-read bytes. 449 * The idea we use here is that n multibyte characters are stored 450 * in no less than n but less than n*MB_CUR_MAX bytes. 451 */ 452 assert(nchreq <= BUFSIZ); 453 delta = 0; 454 p = s = chbuf; 455 t = buf; 456 while (nchread < nchreq) { 457 int m; /* # of bytes to try to read this time. */ 458 int k; /* # of bytes successfully read. */ 459 460 retry: 461 /* 462 * Let's say the (N+1)'th byte bN is actually the first 463 * byte of a three-byte character c. 464 * In that case, p, s, q look like this: 465 * 466 * /-- already read--\ /-- not yet read --\ 467 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... 468 * ^ ^ ^ 469 * | | | 470 * p s q 471 * \----------/ 472 * c hasn't been completed 473 * 474 * Just after the next read(), p and q will be adavanced to: 475 * 476 * /-- already read-----------------------\ /-- not yet - 477 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2... 478 * ^ ^ ^ 479 * | | | 480 * s p q 481 * \----------/ 482 * c has been completed 483 * but hasn't been scanned 484 */ 485 m = nchreq - nchread; 486 assert(p + m < chbuf + sizeof (chbuf)); 487 k = read(d, p, m); 488 /* 489 * when child sets O_NDELAY or O_NONBLOCK on stdin 490 * and exits and we are interactive then turn the modes off 491 * and retry 492 */ 493 if (k == 0) { 494 if ((intty && !onelflg && !cflg) && 495 ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { 496 fflags &= ~O_NDELAY; 497 fcntl(d, F_SETFL, fflags); 498 goto retry; 499 } 500 } else if (k < 0) { 501 if (errno == EAGAIN) { 502 fflags = fcntl(d, F_GETFL, 0); 503 fflags &= ~O_NONBLOCK; 504 fcntl(d, F_SETFL, fflags); 505 goto retry; 506 } 507 return (-1); 508 } 509 nbytread += k; 510 q = p + k; 511 delta = 0; 512 513 /* Try scaning characters in s..q-1 */ 514 while (s < q) { 515 /* Convert the collected bytes into tchar array. */ 516 if (*s == 0) { 517 /* NUL is treated as a normal char here. */ 518 *t++ = 0; 519 s++; 520 nchread++; 521 continue; 522 } 523 524 if ((b_len = q - s) > (int)MB_CUR_MAX) { 525 b_len = MB_CUR_MAX; 526 } 527 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 528 if (b_len < (unsigned int)MB_CUR_MAX) { 529 /* 530 * Needs more byte to complete this char 531 * In order to read() more than delta 532 * bytes. 533 */ 534 break; 535 } 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) > (int)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 ((delta = q - s) == 0) { 567 return (nchread); 568 } 569 570 if (*(s + delta - 1) == '\n') { 571 while (s < q) { 572 if ((b_len = q - s) > (int)MB_CUR_MAX) { 573 b_len = MB_CUR_MAX; 574 } 575 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 576 wc = (unsigned char)*s; 577 j = 1; 578 } 579 *t++ = wc; 580 nchread++; 581 s += j; 582 } 583 return (nchread); 584 } 585 586 for (; delta < (int)MB_CUR_MAX; delta++, q++) { 587 assert((q + 1) < (chbuf + sizeof (chbuf))); 588 if (read(d, q, 1) != 1) { 589 break; 590 } 591 if (*q == '\n') { 592 break; 593 } 594 if (mbtowc(&wc, (char *)s, delta) > 0) { 595 *t = wc; 596 return (nchread + 1); 597 } 598 } 599 600 while (s < q) { 601 if ((b_len = q - s) > (int)MB_CUR_MAX) { 602 b_len = MB_CUR_MAX; 603 } 604 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 605 wc = (unsigned char)*s; 606 j = 1; 607 } 608 *t++ = wc; 609 nchread++; 610 s += j; 611 } 612 return (nchread); 613 #else /* !MBCHAR */ 614 /* One byte always represents one tchar. Easy! */ 615 int i; 616 unsigned char *s; 617 tchar *t; 618 int nchread; 619 620 #ifdef DBG 621 tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", 622 d, buf, nchreq); 623 #endif /* DBG */ 624 assert(nchreq <= BUFSIZ); 625 retry: 626 nchread = read(d, (char *)chbuf, nchreq); 627 /* 628 * when child sets O_NDELAY or O_NONBLOCK on stdin 629 * and exits and we are interactive then turn the modes off 630 * and retry 631 */ 632 if (nchread == 0) { 633 if ((intty && !onelflg && !cflg) && 634 ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { 635 fflags &= ~O_NDELAY; 636 fcntl(d, F_SETFL, fflags); 637 goto retry; 638 } 639 } else if (nchread < 0) { 640 if (errno == EAGAIN) { 641 fflags = fcntl(d, F_GETFL, 0); 642 fflags &= ~O_NONBLOCK; 643 fcntl(d, F_SETFL, fflags); 644 goto retry; 645 } 646 len = 0; 647 } else { 648 for (i = 0, t = buf, s = chbuf; i < nchread; ++i) { 649 *t++ = ((tchar)*s++); 650 } 651 } 652 return (nchread); 653 #endif 654 } 655 656 /* 657 * BUG: write_() returns -1 on failure, or # of BYTEs it has written. 658 * For consistency and symmetry, it should return the number of 659 * characters it has actually written, but that is technically 660 * difficult although not impossible. Anyway, the return 661 * value of write() has never been used by the original csh, 662 * so this bug should be OK. 663 */ 664 int 665 write_(int d, tchar *buf, int nch) 666 { 667 unsigned char chbuf[BUFSIZ*MB_LEN_MAX]; /* General use buffer. */ 668 #ifdef MBCHAR 669 tchar *pt; 670 unsigned char *pc; 671 wchar_t wc; 672 int i, j; 673 674 #ifdef DBG 675 tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", 676 d, buf, nch); /* Hope printf() doesn't call write_() itself! */ 677 #endif /* DBG */ 678 assert(nch * MB_CUR_MAX < sizeof (chbuf)); 679 i = nch; 680 pt = buf; 681 pc = chbuf; 682 while (i--) { 683 /* 684 * Convert to tchar string. 685 * NUL is treated as normal char here. 686 */ 687 wc = (wchar_t)((*pt++)&TRIM); 688 if (wc == (wchar_t)0) { 689 *pc++ = 0; 690 } else { 691 if ((j = wctomb((char *)pc, wc)) <= 0) { 692 *pc = (unsigned char)wc; 693 j = 1; 694 } 695 pc += j; 696 } 697 } 698 return (write(d, chbuf, pc - chbuf)); 699 #else /* !MBCHAR */ 700 /* One byte always represents one tchar. Easy! */ 701 int i; 702 unsigned char *s; 703 tchar *t; 704 705 #ifdef DBG 706 tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", 707 d, buf, nch); /* Hope printf() doesn't call write_() itself! */ 708 #endif /* DBG */ 709 assert(nch <= sizeof (chbuf)); 710 for (i = 0, t = buf, s = chbuf; i < nch; ++i) { 711 *s++ = (char)((*t++)&0xff); 712 } 713 return (write(d, (char *)chbuf, nch)); 714 #endif 715 } 716 717 #undef chbuf 718 719 #include <sys/types.h> 720 #include <sys/stat.h> /* satruct stat */ 721 #include <dirent.h> /* DIR */ 722 723 extern DIR *Dirp; 724 725 int 726 stat_(tchar *path, struct stat *buf) 727 { 728 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 729 730 tstostr(chbuf, path); 731 return (stat((char *)chbuf, buf)); 732 } 733 734 int 735 lstat_(tchar *path, struct stat *buf) 736 { 737 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 738 739 tstostr(chbuf, path); 740 return (lstat((char *)chbuf, buf)); 741 } 742 743 int 744 chdir_(tchar *path) 745 { 746 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 747 748 tstostr(chbuf, path); 749 return (chdir((char *)chbuf)); 750 } 751 752 tchar * 753 getwd_(tchar *path) 754 { 755 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 756 int rc; 757 758 rc = (int)getwd((char *)chbuf); 759 if (rc == 0) { 760 return (0); 761 } else { 762 return (strtots(path, chbuf)); 763 } 764 } 765 766 int 767 unlink_(tchar *path) 768 { 769 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 770 771 tstostr(chbuf, path); 772 return (unlink((char *)chbuf)); 773 } 774 775 DIR * 776 opendir_(tchar *dirname) 777 { 778 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 779 780 extern DIR *opendir(); 781 DIR *dir; 782 783 dir = opendir(tstostr(chbuf, dirname)); 784 if (dir != NULL) { 785 setfd(dir->dd_fd); 786 } 787 return (Dirp = dir); 788 } 789 790 int 791 closedir_(DIR *dirp) 792 { 793 int ret; 794 extern int closedir(); 795 796 ret = closedir(dirp); 797 Dirp = NULL; 798 return (ret); 799 } 800 801 int 802 gethostname_(tchar *name, int namelen) 803 { 804 char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ 805 806 assert(namelen < BUFSIZ); 807 if (gethostname((char *)chbuf, sizeof (chbuf)) != 0) { 808 return (-1); 809 } 810 if (mbstotcs(name, chbuf, namelen) < 0) { 811 return (-1); 812 } 813 return (0); /* Succeeded. */ 814 } 815 816 int 817 readlink_(tchar *path, tchar *buf, int bufsiz) 818 { 819 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 820 char chpath[MAXPATHLEN + 1]; 821 int i; 822 823 tstostr(chpath, path); 824 i = readlink(chpath, (char *)chbuf, sizeof (chbuf)); 825 if (i < 0) { 826 return (-1); 827 } 828 chbuf[i] = (char)0; /* readlink() doesn't put NULL. */ 829 i = mbstotcs(buf, chbuf, bufsiz); 830 if (i < 0) { 831 return (-1); 832 } 833 return (i - 1); /* Return # of tchars EXCLUDING the terminating NULL. */ 834 } 835 836 int 837 atoi_(tchar *str) 838 { 839 char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ 840 841 tstostr(chbuf, str); 842 return (atoi((char *)chbuf)); 843 } 844 845 tchar * 846 simple(tchar *s) 847 { 848 tchar *sname = s; 849 850 while (1) { 851 if (any('/', sname)) { 852 while (*sname++ != '/') 853 ; 854 } else { 855 return (sname); 856 } 857 } 858 } 859