1 /* 2 * Copyright 2000 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 tswidth(tchar *ts) 305 { 306 #ifdef MBCHAR 307 wchar_t tc; 308 int w = 0; 309 int p_col; 310 311 while (tc = *ts++) { 312 if ((p_col = wcwidth((wchar_t)tc)) > 0) 313 w += p_col; 314 } 315 return (w); 316 #else /* !MBCHAR --- one char always occupies one column. */ 317 return (strlen_(ts)); 318 #endif 319 } 320 321 /* 322 * Two getenv() substitute functions. They differ in the type of arguments. 323 * BUGS: Both returns the pointer to an allocated space where the env var's 324 * values is stored. This space is freed automatically on the successive 325 * call of either function. Therefore the caller must copy the contents 326 * if it needs to access two env vars. There is an arbitary limitation 327 * on the number of chars of a env var name. 328 */ 329 #define LONGEST_ENVVARNAME 256 /* Too big? */ 330 tchar * 331 getenv_(tchar *name_) 332 { 333 char name[LONGEST_ENVVARNAME * MB_LEN_MAX]; 334 335 assert(strlen_(name_) < LONGEST_ENVVARNAME); 336 return (getenvs_(tstostr(name, name_))); 337 } 338 339 tchar * 340 getenvs_(char *name) 341 { 342 static tchar *pbuf = (tchar *)NULL; 343 char *val; 344 345 if (pbuf) { 346 XFREE((void *)pbuf); 347 pbuf = NOSTR; 348 } 349 val = getenv(name); 350 if (val == (char *)NULL) { 351 return (NOSTR); 352 } 353 return (pbuf = strtots(NOSTR, val)); 354 } 355 356 /* Followings are the system call interface for tchar strings. */ 357 358 /* 359 * creat() and open() replacement. 360 * BUGS: An unusually long file name could be dangerous. 361 */ 362 int 363 creat_(tchar *name_, int mode) 364 { 365 int fd; 366 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 367 368 tstostr(chbuf, name_); 369 fd = creat((char *)chbuf, mode); 370 if (fd != -1) { 371 setfd(fd); 372 } 373 return (fd); 374 } 375 376 /*VARARGS2*/ 377 int 378 open_(path_, flags, mode) 379 tchar *path_; 380 int flags; 381 int mode; /* May be omitted. */ 382 { 383 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 384 int fd; 385 386 tstostr(chbuf, path_); 387 fd = open((char *)chbuf, flags, mode); 388 if (fd != -1) { 389 setfd(fd); 390 } 391 return (fd); 392 } 393 394 /* 395 * mkstemp replacement 396 */ 397 int 398 mkstemp_(tchar *name_) 399 { 400 int fd; 401 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 402 403 tstostr(chbuf, name_); 404 fd = mkstemp((char *)chbuf); 405 if (fd != -1) { 406 setfd(fd); 407 strtots(name_, chbuf); 408 } 409 return (fd); 410 } 411 412 /* 413 * read() and write() reaplacement. 414 * int d; 415 * tchar *buf; - where the result be stored. Not NULL terminated. 416 * int nchreq; - # of tchars requrested. 417 */ 418 int 419 read_(int d, tchar *buf, int nchreq) 420 { 421 unsigned char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ 422 #ifdef MBCHAR 423 /* 424 * We would have to read more than tchar bytes 425 * when there are multibyte characters in the file. 426 */ 427 int i, j, fflags; 428 unsigned char *s; /* Byte being scanned for a multibyte char. */ 429 /* Points to the pos where next read() to read the data into. */ 430 unsigned char *p; 431 tchar *t; 432 wchar_t wc; 433 int b_len; 434 int nchread = 0; /* Count how many bytes has been read. */ 435 int nbytread = 0; /* Total # of bytes read. */ 436 /* # of bytes needed to complete the last char just read. */ 437 int delta; 438 unsigned char *q; /* q points to the first invalid byte. */ 439 #ifdef DBG 440 tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", 441 d, buf, nchreq); 442 #endif /* DBG */ 443 /* 444 * Step 1: We collect the exact number of bytes that make 445 * nchreq characters into chbuf. 446 * We must be careful not to read too many bytes as we 447 * cannot push back such over-read bytes. 448 * The idea we use here is that n multibyte characters are stored 449 * in no less than n but less than n*MB_CUR_MAX bytes. 450 */ 451 assert(nchreq <= BUFSIZ); 452 delta = 0; 453 p = s = chbuf; 454 t = buf; 455 while (nchread < nchreq) { 456 int m; /* # of bytes to try to read this time. */ 457 int k; /* # of bytes successfully read. */ 458 459 retry: 460 /* 461 * Let's say the (N+1)'th byte bN is actually the first 462 * byte of a three-byte character c. 463 * In that case, p, s, q look like this: 464 * 465 * /-- already read--\ /-- not yet read --\ 466 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... 467 * ^ ^ ^ 468 * | | | 469 * p s q 470 * \----------/ 471 * c hasn't been completed 472 * 473 * Just after the next read(), p and q will be adavanced to: 474 * 475 * /-- already read-----------------------\ /-- not yet - 476 * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2... 477 * ^ ^ ^ 478 * | | | 479 * s p q 480 * \----------/ 481 * c has been completed 482 * but hasn't been scanned 483 */ 484 m = nchreq - nchread; 485 assert(p + m < chbuf + sizeof (chbuf)); 486 k = read(d, p, m); 487 /* 488 * when child sets O_NDELAY or O_NONBLOCK on stdin 489 * and exits and we are interactive then turn the modes off 490 * and retry 491 */ 492 if (k == 0) { 493 if ((intty && !onelflg && !cflg) && 494 ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { 495 fflags &= ~O_NDELAY; 496 fcntl(d, F_SETFL, fflags); 497 goto retry; 498 } 499 } else if (k < 0) { 500 if (errno == EAGAIN) { 501 fflags = fcntl(d, F_GETFL, 0); 502 fflags &= ~O_NONBLOCK; 503 fcntl(d, F_SETFL, fflags); 504 goto retry; 505 } 506 return (-1); 507 } 508 nbytread += k; 509 q = p + k; 510 delta = 0; 511 512 /* Try scaning characters in s..q-1 */ 513 while (s < q) { 514 /* Convert the collected bytes into tchar array. */ 515 if (*s == 0) { 516 /* NUL is treated as a normal char here. */ 517 *t++ = 0; 518 s++; 519 nchread++; 520 continue; 521 } 522 523 if ((b_len = q - s) > (int)MB_CUR_MAX) { 524 b_len = MB_CUR_MAX; 525 } 526 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 527 if (b_len < (unsigned int)MB_CUR_MAX) { 528 /* 529 * Needs more byte to complete this char 530 * In order to read() more than delta 531 * bytes. 532 */ 533 break; 534 } 535 536 wc = (unsigned char)*s; 537 j = 1; 538 } 539 540 *t++ = wc; 541 nchread++; 542 s += j; 543 } 544 545 if (k < m) { 546 /* We've read as many bytes as possible. */ 547 while (s < q) { 548 if ((b_len = q - s) > (int)MB_CUR_MAX) { 549 b_len = MB_CUR_MAX; 550 } 551 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 552 wc = (unsigned char)*s; 553 j = 1; 554 } 555 *t++ = wc; 556 nchread++; 557 s += j; 558 } 559 return (nchread); 560 } 561 562 p = q; 563 } 564 565 if ((delta = q - s) == 0) { 566 return (nchread); 567 } 568 569 if (*(s + delta - 1) == '\n') { 570 while (s < q) { 571 if ((b_len = q - s) > (int)MB_CUR_MAX) { 572 b_len = MB_CUR_MAX; 573 } 574 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 575 wc = (unsigned char)*s; 576 j = 1; 577 } 578 *t++ = wc; 579 nchread++; 580 s += j; 581 } 582 return (nchread); 583 } 584 585 for (; delta < (int)MB_CUR_MAX; delta++, q++) { 586 assert((q + 1) < (chbuf + sizeof (chbuf))); 587 if (read(d, q, 1) != 1) { 588 break; 589 } 590 if (*q == '\n') { 591 break; 592 } 593 if (mbtowc(&wc, (char *)s, delta) > 0) { 594 *t = wc; 595 return (nchread + 1); 596 } 597 } 598 599 while (s < q) { 600 if ((b_len = q - s) > (int)MB_CUR_MAX) { 601 b_len = MB_CUR_MAX; 602 } 603 if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { 604 wc = (unsigned char)*s; 605 j = 1; 606 } 607 *t++ = wc; 608 nchread++; 609 s += j; 610 } 611 return (nchread); 612 #else /* !MBCHAR */ 613 /* One byte always represents one tchar. Easy! */ 614 int i; 615 unsigned char *s; 616 tchar *t; 617 int nchread; 618 619 #ifdef DBG 620 tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", 621 d, buf, nchreq); 622 #endif /* DBG */ 623 assert(nchreq <= BUFSIZ); 624 retry: 625 nchread = read(d, (char *)chbuf, nchreq); 626 /* 627 * when child sets O_NDELAY or O_NONBLOCK on stdin 628 * and exits and we are interactive then turn the modes off 629 * and retry 630 */ 631 if (nchread == 0) { 632 if ((intty && !onelflg && !cflg) && 633 ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { 634 fflags &= ~O_NDELAY; 635 fcntl(d, F_SETFL, fflags); 636 goto retry; 637 } 638 } else if (nchread < 0) { 639 if (errno == EAGAIN) { 640 fflags = fcntl(d, F_GETFL, 0); 641 fflags &= ~O_NONBLOCK; 642 fcntl(d, F_SETFL, fflags); 643 goto retry; 644 } 645 len = 0; 646 } else { 647 for (i = 0, t = buf, s = chbuf; i < nchread; ++i) { 648 *t++ = ((tchar)*s++); 649 } 650 } 651 return (nchread); 652 #endif 653 } 654 655 /* 656 * BUG: write_() returns -1 on failure, or # of BYTEs it has written. 657 * For consistency and symmetry, it should return the number of 658 * characters it has actually written, but that is technically 659 * difficult although not impossible. Anyway, the return 660 * value of write() has never been used by the original csh, 661 * so this bug should be OK. 662 */ 663 int 664 write_(int d, tchar *buf, int nch) 665 { 666 unsigned char chbuf[BUFSIZ*MB_LEN_MAX]; /* General use buffer. */ 667 #ifdef MBCHAR 668 tchar *pt; 669 unsigned char *pc; 670 wchar_t wc; 671 int i, j; 672 673 #ifdef DBG 674 tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", 675 d, buf, nch); /* Hope printf() doesn't call write_() itself! */ 676 #endif /* DBG */ 677 assert(nch * MB_CUR_MAX < sizeof (chbuf)); 678 i = nch; 679 pt = buf; 680 pc = chbuf; 681 while (i--) { 682 /* 683 * Convert to tchar string. 684 * NUL is treated as normal char here. 685 */ 686 wc = (wchar_t)((*pt++)&TRIM); 687 if (wc == (wchar_t)0) { 688 *pc++ = 0; 689 } else { 690 if ((j = wctomb((char *)pc, wc)) <= 0) { 691 *pc = (unsigned char)wc; 692 j = 1; 693 } 694 pc += j; 695 } 696 } 697 return (write(d, chbuf, pc - chbuf)); 698 #else /* !MBCHAR */ 699 /* One byte always represents one tchar. Easy! */ 700 int i; 701 unsigned char *s; 702 tchar *t; 703 704 #ifdef DBG 705 tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", 706 d, buf, nch); /* Hope printf() doesn't call write_() itself! */ 707 #endif /* DBG */ 708 assert(nch <= sizeof (chbuf)); 709 for (i = 0, t = buf, s = chbuf; i < nch; ++i) { 710 *s++ = (char)((*t++)&0xff); 711 } 712 return (write(d, (char *)chbuf, nch)); 713 #endif 714 } 715 716 #undef chbuf 717 718 #include <sys/types.h> 719 #include <sys/stat.h> /* satruct stat */ 720 #include <dirent.h> /* DIR */ 721 722 extern DIR *Dirp; 723 724 int 725 stat_(tchar *path, struct stat *buf) 726 { 727 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 728 729 tstostr(chbuf, path); 730 return (stat((char *)chbuf, buf)); 731 } 732 733 int 734 lstat_(tchar *path, struct stat *buf) 735 { 736 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 737 738 tstostr(chbuf, path); 739 return (lstat((char *)chbuf, buf)); 740 } 741 742 int 743 chdir_(tchar *path) 744 { 745 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 746 747 tstostr(chbuf, path); 748 return (chdir((char *)chbuf)); 749 } 750 751 tchar * 752 getwd_(tchar *path) 753 { 754 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 755 int rc; 756 757 rc = (int)getwd((char *)chbuf); 758 if (rc == 0) { 759 return (0); 760 } else { 761 return (strtots(path, chbuf)); 762 } 763 } 764 765 int 766 unlink_(tchar *path) 767 { 768 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 769 770 tstostr(chbuf, path); 771 return (unlink((char *)chbuf)); 772 } 773 774 DIR * 775 opendir_(tchar *dirname) 776 { 777 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 778 779 extern DIR *opendir(); 780 DIR *dir; 781 782 dir = opendir(tstostr(chbuf, dirname)); 783 if (dir != NULL) { 784 setfd(dir->dd_fd); 785 } 786 return (Dirp = dir); 787 } 788 789 int 790 closedir_(DIR *dirp) 791 { 792 int ret; 793 extern int closedir(); 794 795 ret = closedir(dirp); 796 Dirp = NULL; 797 return (ret); 798 } 799 800 int 801 gethostname_(tchar *name, int namelen) 802 { 803 char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ 804 805 assert(namelen < BUFSIZ); 806 if (gethostname((char *)chbuf, sizeof (chbuf)) != 0) { 807 return (-1); 808 } 809 if (mbstotcs(name, chbuf, namelen) < 0) { 810 return (-1); 811 } 812 return (0); /* Succeeded. */ 813 } 814 815 int 816 readlink_(tchar *path, tchar *buf, int bufsiz) 817 { 818 char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ 819 char chpath[MAXPATHLEN + 1]; 820 int i; 821 822 tstostr(chpath, path); 823 i = readlink(chpath, (char *)chbuf, sizeof (chbuf)); 824 if (i < 0) { 825 return (-1); 826 } 827 chbuf[i] = (char)0; /* readlink() doesn't put NULL. */ 828 i = mbstotcs(buf, chbuf, bufsiz); 829 if (i < 0) { 830 return (-1); 831 } 832 return (i - 1); /* Return # of tchars EXCLUDING the terminating NULL. */ 833 } 834 835 int 836 atoi_(tchar *str) 837 { 838 char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ 839 840 tstostr(chbuf, str); 841 return (atoi((char *)chbuf)); 842 } 843 844 tchar * 845 simple(tchar *s) 846 { 847 register tchar *sname = s; 848 849 while (1) { 850 if (any('/', sname)) { 851 while (*sname++ != '/') 852 ; 853 } else { 854 return (sname); 855 } 856 } 857 } 858