1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 31 /* All Rights Reserved */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 /* 36 * List files or directories 37 */ 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 #include <sys/mkdev.h> 42 #include <sys/stat.h> 43 #include <sys/acl.h> 44 45 #include <wchar.h> 46 #include <stdio.h> 47 #include <ctype.h> 48 #include <dirent.h> 49 #include <string.h> 50 #include <locale.h> 51 #include <curses.h> 52 #include <termios.h> 53 #include <stdlib.h> 54 #include <widec.h> 55 #include <locale.h> 56 #include <wctype.h> 57 #include <pwd.h> 58 #include <grp.h> 59 #include <limits.h> 60 #include <fcntl.h> 61 #include <unistd.h> 62 #include <libgen.h> 63 #include <errno.h> 64 #include <libcmdutils.h> 65 66 #ifndef STANDALONE 67 #define TERMINFO 68 #endif 69 70 /* 71 * -DNOTERMINFO can be defined on the cc command line to prevent 72 * the use of terminfo. This should be done on systems not having 73 * the terminfo feature(pre 6.0 sytems ?). 74 * As a result, columnar listings assume 80 columns for output, 75 * unless told otherwise via the COLUMNS environment variable. 76 */ 77 #ifdef NOTERMINFO 78 #undef TERMINFO 79 #endif 80 81 #include <term.h> 82 83 #define BFSIZE 16 84 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */ 85 #define ISARG 0100000 86 87 /* 88 * this flag has been added to manipulate the display of S instead of 'l' when 89 * the file is not a regular file and when group execution bit is off 90 */ 91 #define LS_NOTREG 010000 92 93 94 /* 95 * Date and time formats 96 * 97 * b --- abbreviated month name 98 * e --- day number 99 * Y --- year in the form ccyy 100 * H --- hour(24-hour version) 101 * M --- minute 102 * F --- yyyy-mm-dd 103 * T --- hh:mm:ss 104 * z --- time zone as hours displacement from UTC 105 * note that %F and %z are from the ISO C99 standard and are 106 * not present in older C libraries 107 */ 108 #define FORMAT1 " %b %e %Y " 109 #define FORMAT2 " %b %e %H:%M " 110 #define FORMAT3 " %b %e %T %Y " 111 #define FORMAT4 " %%F %%T.%.09ld %%z " 112 113 #undef BUFSIZ 114 #define BUFSIZ 4096 115 #define NUMBER_WIDTH 40 116 #define FMTSIZE 50 117 118 struct ditem { 119 dev_t dev; /* directory items device number */ 120 ino_t ino; /* directory items inode number */ 121 struct ditem *parent; /* dir items ptr to its parent's info */ 122 }; 123 124 struct lbuf { 125 union { 126 char lname[MAXNAMLEN]; /* used for filename in a directory */ 127 char *namep; /* for name in ls-command; */ 128 } ln; 129 char ltype; /* filetype */ 130 ino_t lnum; /* inode number of file */ 131 mode_t lflags; /* 0777 bits used as r,w,x permissions */ 132 nlink_t lnl; /* number of links to file */ 133 uid_t luid; 134 gid_t lgid; 135 off_t lsize; /* filesize or major/minor dev numbers */ 136 blkcnt_t lblocks; /* number of file blocks */ 137 timestruc_t lmtime; 138 char *flinkto; /* symbolic link contents */ 139 char acl; /* indicate there are additional acl entries */ 140 int cycle; /* cycle detected flag */ 141 struct ditem *ancinfo; /* maintains ancestor info */ 142 }; 143 144 struct dchain { 145 char *dc_name; /* path name */ 146 int cycle_detected; /* cycle detected visiting this directory */ 147 struct ditem *myancinfo; /* this directory's ancestry info */ 148 struct dchain *dc_next; /* next directory in the chain */ 149 }; 150 151 /* 152 * A numbuf_t is used when converting a number to a string representation 153 */ 154 typedef char numbuf_t[NUMBER_WIDTH]; 155 156 static struct dchain *dfirst; /* start of the dir chain */ 157 static struct dchain *cdfirst; /* start of the current dir chain */ 158 static struct dchain *dtemp; /* temporary - used for linking */ 159 static char *curdir; /* the current directory */ 160 161 static int first = 1; /* true if first line is not yet printed */ 162 static int nfiles = 0; /* number of flist entries in current use */ 163 static int nargs = 0; /* number of flist entries used for arguments */ 164 static int maxfils = 0; /* number of flist/lbuf entries allocated */ 165 static int maxn = 0; /* number of flist entries with lbufs asigned */ 166 static int quantn = 64; /* allocation growth quantum */ 167 168 static struct lbuf *nxtlbf; /* ptr to next lbuf to be assigned */ 169 static struct lbuf **flist; /* ptr to list of lbuf pointers */ 170 static struct lbuf *gstat(char *, int, struct ditem *); 171 static char *getname(uid_t); 172 static char *getgroup(gid_t); 173 static char *makename(char *, char *); 174 static void pentry(struct lbuf *); 175 static void column(void); 176 static void pmode(mode_t aflag); 177 static void selection(int *); 178 static void new_line(void); 179 static void rddir(char *, struct ditem *); 180 static int strcol(unsigned char *); 181 static void pem(struct lbuf **, struct lbuf **, int); 182 static void pdirectory(char *, int, int, int, struct ditem *); 183 static struct cachenode *findincache(struct cachenode **, long); 184 static void csi_pprintf(unsigned char *); 185 static void pprintf(char *, char *); 186 static int compar(struct lbuf **pp1, struct lbuf **pp2); 187 static char *number_to_scaled_string(numbuf_t buf, 188 unsigned long long number, 189 long scale); 190 static void record_ancestry(char *, struct stat *, struct lbuf *, 191 int, struct ditem *); 192 193 static int aflg; 194 static int atflg; 195 static int bflg; 196 static int cflg; 197 static int dflg; 198 static int eflg; 199 static int fflg; 200 static int gflg; 201 static int hflg; 202 static int iflg; 203 static int lflg; 204 static int mflg; 205 static int nflg; 206 static int oflg; 207 static int pflg; 208 static int qflg; 209 static int rflg = 1; /* init to 1 for special use in compar */ 210 static int sflg; 211 static int tflg; 212 static int uflg; 213 static int xflg; 214 static int Aflg; 215 static int Cflg; 216 static int Eflg; 217 static int Fflg; 218 static int Hflg; 219 static int Lflg; 220 static int Rflg; 221 static int Sflg; 222 static long hscale; 223 static mode_t flags; 224 static int err = 0; /* Contains return code */ 225 226 static uid_t lastuid = (uid_t)-1; 227 static gid_t lastgid = (gid_t)-1; 228 static char *lastuname = NULL; 229 static char *lastgname = NULL; 230 231 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg are on */ 232 static int statreq; 233 234 static char *dotp = "."; 235 236 static u_longlong_t tblocks; /* number of blocks of files in a directory */ 237 static time_t year, now; 238 239 static int num_cols = 80; 240 static int colwidth; 241 static int filewidth; 242 static int fixedwidth; 243 static int nomocore; 244 static int curcol; 245 246 static struct winsize win; 247 248 static char time_buf[50]; /* array to hold day and time */ 249 250 #define NOTWORKINGDIR(d, l) (((l) < 2) || \ 251 (strcmp((d) + (l) - 2, "/.") != 0)) 252 253 #define NOTPARENTDIR(d, l) (((l) < 3) || \ 254 (strcmp((d) + (l) - 3, "/..") != 0)) 255 256 int 257 main(int argc, char *argv[]) 258 { 259 int c; 260 int i; 261 int width; 262 int amino = 0; 263 int opterr = 0; 264 struct lbuf *ep; 265 struct lbuf lb; 266 struct ditem *myinfo; 267 268 (void) setlocale(LC_ALL, ""); 269 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 270 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 271 #endif 272 (void) textdomain(TEXT_DOMAIN); 273 #ifdef STANDALONE 274 if (argv[0][0] == '\0') 275 argc = getargv("ls", &argv, 0); 276 #endif 277 278 lb.lmtime.tv_sec = time(NULL); 279 lb.lmtime.tv_nsec = 0; 280 year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */ 281 now = lb.lmtime.tv_sec + 60; 282 if (isatty(1)) { 283 Cflg = 1; 284 mflg = 0; 285 } 286 287 288 while ((c = getopt(argc, argv, 289 "aAbcCdeEfFghHilLmnopqrRsStux1@")) != EOF) 290 switch (c) { 291 case 'a': 292 aflg++; 293 continue; 294 case 'A': 295 Aflg++; 296 continue; 297 case 'b': 298 bflg = 1; 299 qflg = 0; 300 continue; 301 case 'c': 302 uflg = 0; 303 cflg++; 304 continue; 305 case 'C': 306 Cflg = 1; 307 mflg = 0; 308 #ifdef XPG4 309 lflg = 0; 310 #endif 311 continue; 312 case 'd': 313 dflg++; 314 continue; 315 case 'e': 316 eflg++; 317 lflg++; 318 statreq++; 319 Eflg = 0; 320 continue; 321 case 'E': 322 Eflg++; 323 lflg++; 324 statreq++; 325 eflg = 0; 326 continue; 327 case 'f': 328 fflg++; 329 continue; 330 case 'F': 331 Fflg++; 332 statreq++; 333 continue; 334 case 'g': 335 gflg++; 336 lflg++; 337 statreq++; 338 continue; 339 case 'h': 340 hflg++; 341 hscale = 1024; 342 continue; 343 case 'H': 344 Hflg++; 345 /* -H and -L are mutually exclusive */ 346 Lflg = 0; 347 continue; 348 case 'i': 349 iflg++; 350 continue; 351 case 'l': 352 lflg++; 353 statreq++; 354 Cflg = 0; 355 xflg = 0; 356 mflg = 0; 357 atflg = 0; 358 continue; 359 case 'L': 360 Lflg++; 361 /* -H and -L are mutually exclusive */ 362 Hflg = 0; 363 continue; 364 case 'm': 365 Cflg = 0; 366 mflg = 1; 367 #ifdef XPG4 368 lflg = 0; 369 #endif 370 continue; 371 case 'n': 372 nflg++; 373 lflg++; 374 statreq++; 375 Cflg = 0; 376 xflg = 0; 377 mflg = 0; 378 atflg = 0; 379 continue; 380 case 'o': 381 oflg++; 382 lflg++; 383 statreq++; 384 continue; 385 case 'p': 386 pflg++; 387 statreq++; 388 continue; 389 case 'q': 390 qflg = 1; 391 bflg = 0; 392 continue; 393 case 'r': 394 rflg = -1; 395 continue; 396 case 'R': 397 Rflg++; 398 statreq++; 399 continue; 400 case 's': 401 sflg++; 402 statreq++; 403 continue; 404 case 'S': 405 tflg = 0; 406 Sflg++; 407 statreq++; 408 continue; 409 case 't': 410 Sflg = 0; 411 tflg++; 412 statreq++; 413 continue; 414 case 'u': 415 cflg = 0; 416 uflg++; 417 continue; 418 case 'x': 419 xflg = 1; 420 Cflg = 1; 421 mflg = 0; 422 #ifdef XPG4 423 lflg = 0; 424 #endif 425 continue; 426 case '1': 427 Cflg = 0; 428 continue; 429 case '@': 430 #if !defined(XPG4) 431 /* 432 * -l has precedence over -@ 433 */ 434 if (lflg) 435 continue; 436 #endif 437 atflg++; 438 lflg++; 439 statreq++; 440 Cflg = 0; 441 xflg = 0; 442 mflg = 0; 443 continue; 444 case '?': 445 opterr++; 446 continue; 447 } 448 if (opterr) { 449 (void) fprintf(stderr, gettext( 450 "usage: ls -aAbcCdeEfFghHilLmnopqrRsStux1@ [files]\n")); 451 exit(2); 452 } 453 454 if (fflg) { 455 aflg++; 456 lflg = 0; 457 sflg = 0; 458 tflg = 0; 459 Sflg = 0; 460 statreq = 0; 461 } 462 463 fixedwidth = 2; 464 if (pflg || Fflg) 465 fixedwidth++; 466 if (iflg) 467 fixedwidth += 11; 468 if (sflg) 469 fixedwidth += 5; 470 471 if (lflg) { 472 if (!gflg && !oflg) 473 gflg = oflg = 1; 474 else 475 if (gflg && oflg) 476 gflg = oflg = 0; 477 Cflg = mflg = 0; 478 } 479 480 if (Cflg || mflg) { 481 char *clptr; 482 if ((clptr = getenv("COLUMNS")) != NULL) 483 num_cols = atoi(clptr); 484 #ifdef TERMINFO 485 else { 486 if (ioctl(1, TIOCGWINSZ, &win) != -1) 487 num_cols = (win.ws_col == 0 ? 80 : win.ws_col); 488 } 489 #endif 490 if (num_cols < 20 || num_cols > 1000) 491 /* assume it is an error */ 492 num_cols = 80; 493 } 494 495 /* allocate space for flist and the associated */ 496 /* data structures (lbufs) */ 497 maxfils = quantn; 498 if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) || 499 ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) { 500 perror("ls"); 501 exit(2); 502 } 503 if ((amino = (argc-optind)) == 0) { 504 /* 505 * case when no names are given 506 * in ls-command and current 507 * directory is to be used 508 */ 509 argv[optind] = dotp; 510 } 511 512 for (i = 0; i < (amino ? amino : 1); i++) { 513 514 /* 515 * If we are recursing, we need to make sure we don't 516 * get into an endless loop. To keep track of the inodes 517 * (actually, just the directories) visited, we 518 * maintain a directory ancestry list for a file 519 * hierarchy. As we go deeper into the hierarchy, 520 * a parent directory passes its directory list 521 * info (device id, inode number, and a pointer to 522 * its parent) to each of its children. As we 523 * process a child that is a directory, we save 524 * its own personal directory list info. We then 525 * check to see if the child has already been 526 * processed by comparing its device id and inode 527 * number from its own personal directory list info 528 * to that of each of its ancestors. If there is a 529 * match, then we know we've detected a cycle. 530 */ 531 if (Rflg) { 532 /* 533 * This is the first parent in this lineage 534 * (first in a directory hierarchy), so 535 * this parent's parent doesn't exist. We 536 * only initialize myinfo when we are 537 * recursing, otherwise it's not used. 538 */ 539 if ((myinfo = (struct ditem *)malloc( 540 sizeof (struct ditem))) == NULL) { 541 perror("ls"); 542 exit(2); 543 } else { 544 myinfo->dev = 0; 545 myinfo->ino = 0; 546 myinfo->parent = NULL; 547 } 548 } 549 550 if (Cflg || mflg) { 551 width = strcol((unsigned char *)argv[optind]); 552 if (width > filewidth) 553 filewidth = width; 554 } 555 if ((ep = gstat((*argv[optind] ? argv[optind] : dotp), 556 1, myinfo)) == NULL) { 557 if (nomocore) 558 exit(2); 559 err = 2; 560 optind++; 561 continue; 562 } 563 ep->ln.namep = (*argv[optind] ? argv[optind] : dotp); 564 ep->lflags |= ISARG; 565 optind++; 566 nargs++; /* count good arguments stored in flist */ 567 } 568 colwidth = fixedwidth + filewidth; 569 qsort(flist, (unsigned)nargs, sizeof (struct lbuf *), 570 (int (*)(const void *, const void *))compar); 571 for (i = 0; i < nargs; i++) { 572 if (flist[i]->ltype == 'd' && dflg == 0 || fflg) 573 break; 574 } 575 pem(&flist[0], &flist[i], 0); 576 for (; i < nargs; i++) { 577 pdirectory(flist[i]->ln.namep, Rflg || 578 (amino > 1), nargs, 0, flist[i]->ancinfo); 579 if (nomocore) 580 exit(2); 581 /* -R: print subdirectories found */ 582 while (dfirst || cdfirst) { 583 /* Place direct subdirs on front in right order */ 584 while (cdfirst) { 585 /* reverse cdfirst onto front of dfirst */ 586 dtemp = cdfirst; 587 cdfirst = cdfirst -> dc_next; 588 dtemp -> dc_next = dfirst; 589 dfirst = dtemp; 590 } 591 /* take off first dir on dfirst & print it */ 592 dtemp = dfirst; 593 dfirst = dfirst->dc_next; 594 pdirectory(dtemp->dc_name, 1, nargs, 595 dtemp->cycle_detected, dtemp->myancinfo); 596 if (nomocore) 597 exit(2); 598 free(dtemp->dc_name); 599 free(dtemp); 600 } 601 } 602 return (err); 603 } 604 605 /* 606 * pdirectory: print the directory name, labelling it if title is 607 * nonzero, using lp as the place to start reading in the dir. 608 */ 609 static void 610 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo) 611 { 612 struct dchain *dp; 613 struct lbuf *ap; 614 char *pname; 615 int j; 616 617 filewidth = 0; 618 curdir = name; 619 if (title) { 620 if (!first) 621 (void) putc('\n', stdout); 622 pprintf(name, ":"); 623 new_line(); 624 } 625 /* 626 * If there was a cycle detected, then notify and don't report 627 * further. 628 */ 629 if (cdetect) { 630 if (lflg || sflg) { 631 curcol += printf(gettext("total %d"), 0); 632 new_line(); 633 } 634 (void) fprintf(stderr, gettext( 635 "ls: cycle detected for %s\n"), name); 636 return; 637 } 638 639 nfiles = lp; 640 rddir(name, myinfo); 641 if (nomocore) 642 return; 643 if (fflg == 0) 644 qsort(&flist[lp], (unsigned)(nfiles - lp), 645 sizeof (struct lbuf *), 646 (int (*)(const void *, const void *))compar); 647 if (Rflg) { 648 for (j = nfiles - 1; j >= lp; j--) { 649 ap = flist[j]; 650 if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") && 651 strcmp(ap->ln.lname, "..")) { 652 dp = malloc(sizeof (struct dchain)); 653 if (dp == NULL) { 654 perror("ls"); 655 exit(2); 656 } 657 pname = makename(curdir, ap->ln.lname); 658 if ((dp->dc_name = strdup(pname)) == NULL) { 659 perror("ls"); 660 exit(2); 661 } 662 dp->cycle_detected = ap->cycle; 663 dp->myancinfo = ap->ancinfo; 664 dp->dc_next = dfirst; 665 dfirst = dp; 666 } 667 } 668 } 669 if (lflg || sflg) { 670 curcol += printf(gettext("total %llu"), tblocks); 671 new_line(); 672 } 673 pem(&flist[lp], &flist[nfiles], lflg||sflg); 674 } 675 676 /* 677 * pem: print 'em. Print a list of files (e.g. a directory) bounded 678 * by slp and lp. 679 */ 680 static void 681 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag) 682 { 683 long row, nrows, i; 684 int col, ncols; 685 struct lbuf **ep; 686 687 if (Cflg || mflg) { 688 if (colwidth > num_cols) { 689 ncols = 1; 690 } else { 691 ncols = num_cols / colwidth; 692 } 693 } 694 695 if (ncols == 1 || mflg || xflg || !Cflg) { 696 for (ep = slp; ep < lp; ep++) 697 pentry(*ep); 698 new_line(); 699 return; 700 } 701 /* otherwise print -C columns */ 702 if (tot_flag) { 703 slp--; 704 row = 1; 705 } 706 else 707 row = 0; 708 709 nrows = (lp - slp - 1) / ncols + 1; 710 for (i = 0; i < nrows; i++, row++) { 711 for (col = 0; col < ncols; col++) { 712 ep = slp + (nrows * col) + row; 713 if (ep < lp) 714 pentry(*ep); 715 } 716 new_line(); 717 } 718 } 719 720 /* 721 * print one output entry; 722 * if uid/gid is not found in the appropriate 723 * file(passwd/group), then print uid/gid instead of 724 * user/group name; 725 */ 726 static void 727 pentry(struct lbuf *ap) 728 { 729 struct lbuf *p; 730 numbuf_t hbuf; 731 char buf[BUFSIZ]; 732 char fmt_buf[FMTSIZE]; 733 char *dmark = ""; /* Used if -p or -F option active */ 734 char *cp; 735 736 p = ap; 737 column(); 738 if (iflg) 739 if (mflg && !lflg) 740 curcol += printf("%llu ", (long long)p->lnum); 741 else 742 curcol += printf("%10llu ", (long long)p->lnum); 743 if (sflg) 744 curcol += printf((mflg && !lflg) ? "%lld " : 745 (p->lblocks < 10000) ? "%4lld " : "%lld ", 746 (p->ltype != 'b' && p->ltype != 'c') ? 747 p->lblocks : 0LL); 748 if (lflg) { 749 (void) putchar(p->ltype); 750 curcol++; 751 pmode(p->lflags); 752 753 /* ACL: additional access mode flag */ 754 (void) putchar(p->acl); 755 curcol++; 756 757 curcol += printf("%3lu ", (ulong_t)p->lnl); 758 if (oflg) 759 if (!nflg) { 760 cp = getname(p->luid); 761 curcol += printf("%-8s ", cp); 762 } else 763 curcol += printf("%-8lu ", (ulong_t)p->luid); 764 if (gflg) 765 if (!nflg) { 766 cp = getgroup(p->lgid); 767 curcol += printf("%-8s ", cp); 768 } else 769 curcol += printf("%-8lu ", (ulong_t)p->lgid); 770 if (p->ltype == 'b' || p->ltype == 'c') { 771 curcol += printf("%3u, %2u", 772 (uint_t)major((dev_t)p->lsize), 773 (uint_t)minor((dev_t)p->lsize)); 774 } else if (hflg && (p->lsize >= hscale)) { 775 curcol += printf("%7s", 776 number_to_scaled_string(hbuf, p->lsize, hscale)); 777 } else { 778 curcol += printf((p->lsize < (off_t)10000000) ? 779 "%7lld" : "%lld", p->lsize); 780 } 781 if (eflg) { 782 (void) strftime(time_buf, sizeof (time_buf), 783 dcgettext(NULL, FORMAT3, LC_TIME), 784 localtime(&p->lmtime.tv_sec)); 785 } else if (Eflg) { 786 /* fill in nanoseconds first */ 787 (void) snprintf(fmt_buf, sizeof (fmt_buf), 788 FORMAT4, p->lmtime.tv_nsec); 789 (void) strftime(time_buf, sizeof (time_buf), 790 fmt_buf, localtime(&p->lmtime.tv_sec)); 791 } else { 792 if ((p->lmtime.tv_sec < year) || 793 (p->lmtime.tv_sec > now)) { 794 (void) strftime(time_buf, sizeof (time_buf), 795 dcgettext(NULL, FORMAT1, LC_TIME), 796 localtime(&p->lmtime.tv_sec)); 797 } else { 798 (void) strftime(time_buf, sizeof (time_buf), 799 dcgettext(NULL, FORMAT2, LC_TIME), 800 localtime(&p->lmtime.tv_sec)); 801 } 802 } 803 curcol += printf("%s", time_buf); 804 } 805 806 /* 807 * prevent both "->" and trailing marks 808 * from appearing 809 */ 810 811 if (pflg && p->ltype == 'd') 812 dmark = "/"; 813 814 if (Fflg && !(lflg && p->flinkto)) { 815 if (p->ltype == 'd') 816 dmark = "/"; 817 else if (p->ltype == 'D') 818 dmark = ">"; 819 else if (p->ltype == 'p') 820 dmark = "|"; 821 else if (p->ltype == 'l') 822 dmark = "@"; 823 else if (p->ltype == 's') 824 dmark = "="; 825 else if (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)) 826 dmark = "*"; 827 else 828 dmark = ""; 829 } 830 831 if (lflg && p->flinkto) { 832 (void) strncpy(buf, " -> ", 4); 833 (void) strcpy(buf + 4, p->flinkto); 834 dmark = buf; 835 } 836 837 if (p->lflags & ISARG) { 838 if (qflg || bflg) 839 pprintf(p->ln.namep, dmark); 840 else { 841 (void) printf("%s%s", p->ln.namep, dmark); 842 curcol += strcol((unsigned char *)p->ln.namep); 843 curcol += strcol((unsigned char *)dmark); 844 } 845 } else { 846 if (qflg || bflg) 847 pprintf(p->ln.lname, dmark); 848 else { 849 (void) printf("%s%s", p->ln.lname, dmark); 850 curcol += strcol((unsigned char *)p->ln.lname); 851 curcol += strcol((unsigned char *)dmark); 852 } 853 } 854 } 855 856 /* print various r,w,x permissions */ 857 static void 858 pmode(mode_t aflag) 859 { 860 /* these arrays are declared static to allow initializations */ 861 static int m0[] = { 1, S_IRUSR, 'r', '-' }; 862 static int m1[] = { 1, S_IWUSR, 'w', '-' }; 863 static int m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR, 864 'x', S_ISUID, 'S', '-' }; 865 static int m3[] = { 1, S_IRGRP, 'r', '-' }; 866 static int m4[] = { 1, S_IWGRP, 'w', '-' }; 867 static int m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP, 868 'x', S_ISGID|LS_NOTREG, 'S', 869 #ifdef XPG4 870 S_ISGID, 'L', '-'}; 871 #else 872 S_ISGID, 'l', '-'}; 873 #endif 874 static int m6[] = { 1, S_IROTH, 'r', '-' }; 875 static int m7[] = { 1, S_IWOTH, 'w', '-' }; 876 static int m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH, 877 'x', S_ISVTX, 'T', '-'}; 878 879 static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8}; 880 881 int **mp; 882 883 flags = aflag; 884 for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++) 885 selection(*mp); 886 } 887 888 static void 889 selection(int *pairp) 890 { 891 int n; 892 893 n = *pairp++; 894 while (n-->0) { 895 if ((flags & *pairp) == *pairp) { 896 pairp++; 897 break; 898 } else { 899 pairp += 2; 900 } 901 } 902 (void) putchar(*pairp); 903 curcol++; 904 } 905 906 /* 907 * column: get to the beginning of the next column. 908 */ 909 static void 910 column(void) 911 { 912 if (curcol == 0) 913 return; 914 if (mflg) { 915 (void) putc(',', stdout); 916 curcol++; 917 if (curcol + colwidth + 2 > num_cols) { 918 (void) putc('\n', stdout); 919 curcol = 0; 920 return; 921 } 922 (void) putc(' ', stdout); 923 curcol++; 924 return; 925 } 926 if (Cflg == 0) { 927 (void) putc('\n', stdout); 928 curcol = 0; 929 return; 930 } 931 if ((curcol / colwidth + 2) * colwidth > num_cols) { 932 (void) putc('\n', stdout); 933 curcol = 0; 934 return; 935 } 936 do { 937 (void) putc(' ', stdout); 938 curcol++; 939 } while (curcol % colwidth); 940 } 941 942 static void 943 new_line(void) 944 { 945 if (curcol) { 946 first = 0; 947 (void) putc('\n', stdout); 948 curcol = 0; 949 } 950 } 951 952 /* 953 * read each filename in directory dir and store its 954 * status in flist[nfiles] 955 * use makename() to form pathname dir/filename; 956 */ 957 static void 958 rddir(char *dir, struct ditem *myinfo) 959 { 960 struct dirent *dentry; 961 DIR *dirf; 962 int j; 963 struct lbuf *ep; 964 int width; 965 966 if ((dirf = opendir(dir)) == NULL) { 967 (void) fflush(stdout); 968 perror(dir); 969 err = 2; 970 return; 971 } else { 972 tblocks = 0; 973 for (;;) { 974 errno = 0; 975 if ((dentry = readdir(dirf)) == NULL) 976 break; 977 if (aflg == 0 && dentry->d_name[0] == '.' && 978 (Aflg == 0 || 979 dentry->d_name[1] == '\0' || 980 dentry->d_name[1] == '.' && 981 dentry->d_name[2] == '\0')) 982 /* 983 * check for directory items '.', '..', 984 * and items without valid inode-number; 985 */ 986 continue; 987 988 if (Cflg || mflg) { 989 width = strcol((unsigned char *)dentry->d_name); 990 if (width > filewidth) 991 filewidth = width; 992 } 993 ep = gstat(makename(dir, dentry->d_name), 0, myinfo); 994 if (ep == NULL) { 995 if (nomocore) 996 return; 997 continue; 998 } else { 999 ep->lnum = dentry->d_ino; 1000 for (j = 0; dentry->d_name[j] != '\0'; j++) 1001 ep->ln.lname[j] = dentry->d_name[j]; 1002 ep->ln.lname[j] = '\0'; 1003 } 1004 } 1005 if (errno) { 1006 int sav_errno = errno; 1007 1008 (void) fprintf(stderr, 1009 gettext("ls: error reading directory %s: %s\n"), 1010 dir, strerror(sav_errno)); 1011 } 1012 (void) closedir(dirf); 1013 colwidth = fixedwidth + filewidth; 1014 } 1015 } 1016 1017 /* 1018 * Attaching a link to an inode's ancestors. Search 1019 * through the ancestors to check for cycles (an inode which 1020 * we have already tracked in this inodes ancestry). If a cycle 1021 * is detected, set the exit code and record the fact so that 1022 * it is reported at the right time when printing the directory. 1023 * In addition, set the exit code. Note: If the -a flag was 1024 * specified, we don't want to check for cycles for directories 1025 * ending in '/.' or '/..' unless they were specified on the 1026 * command line. 1027 */ 1028 static void 1029 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep, 1030 int argfl, struct ditem *myparent) 1031 { 1032 size_t file_len; 1033 struct ditem *myinfo; 1034 struct ditem *tptr; 1035 1036 file_len = strlen(file); 1037 if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) && 1038 NOTPARENTDIR(file, file_len))) { 1039 /* 1040 * Add this inode's ancestry 1041 * info and insert it into the 1042 * ancestry list by pointing 1043 * back to its parent. We save 1044 * it (in rep) with the other info 1045 * we're gathering for this inode. 1046 */ 1047 if ((myinfo = malloc( 1048 sizeof (struct ditem))) == NULL) { 1049 perror("ls"); 1050 exit(2); 1051 } 1052 myinfo->dev = pstatb->st_dev; 1053 myinfo->ino = pstatb->st_ino; 1054 myinfo->parent = myparent; 1055 rep->ancinfo = myinfo; 1056 1057 /* 1058 * If this node has the same device id and 1059 * inode number of one of its ancestors, 1060 * then we've detected a cycle. 1061 */ 1062 if (myparent != NULL) { 1063 for (tptr = myparent; tptr->parent != NULL; 1064 tptr = tptr->parent) { 1065 if ((tptr->dev == pstatb->st_dev) && 1066 (tptr->ino == pstatb->st_ino)) { 1067 /* 1068 * Cycle detected for this 1069 * directory. Record the fact 1070 * it is a cycle so we don't 1071 * try to process this 1072 * directory as we are 1073 * walking through the 1074 * list of directories. 1075 */ 1076 rep->cycle = 1; 1077 err = 2; 1078 break; 1079 } 1080 } 1081 } 1082 } 1083 } 1084 1085 /* 1086 * get status of file and recomputes tblocks; 1087 * argfl = 1 if file is a name in ls-command and = 0 1088 * for filename in a directory whose name is an 1089 * argument in the command; 1090 * stores a pointer in flist[nfiles] and 1091 * returns that pointer; 1092 * returns NULL if failed; 1093 */ 1094 static struct lbuf * 1095 gstat(char *file, int argfl, struct ditem *myparent) 1096 { 1097 struct stat statb, statb1; 1098 struct lbuf *rep; 1099 char buf[BUFSIZ]; 1100 ssize_t cc; 1101 int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat; 1102 int aclcnt; 1103 aclent_t *aclp; 1104 aclent_t *tp; 1105 o_mode_t groupperm, mask; 1106 int grouppermfound, maskfound; 1107 1108 if (nomocore) 1109 return (NULL); 1110 1111 if (nfiles >= maxfils) { 1112 /* 1113 * all flist/lbuf pair assigned files, time to get some 1114 * more space 1115 */ 1116 maxfils += quantn; 1117 if (((flist = realloc(flist, 1118 maxfils * sizeof (struct lbuf *))) == NULL) || 1119 ((nxtlbf = malloc(quantn * 1120 sizeof (struct lbuf))) == NULL)) { 1121 perror("ls"); 1122 nomocore = 1; 1123 return (NULL); 1124 } 1125 } 1126 1127 /* 1128 * nfiles is reset to nargs for each directory 1129 * that is given as an argument maxn is checked 1130 * to prevent the assignment of an lbuf to a flist entry 1131 * that already has one assigned. 1132 */ 1133 if (nfiles >= maxn) { 1134 rep = nxtlbf++; 1135 flist[nfiles++] = rep; 1136 maxn = nfiles; 1137 } else { 1138 rep = flist[nfiles++]; 1139 } 1140 rep->lflags = (mode_t)0; 1141 rep->flinkto = NULL; 1142 rep->cycle = 0; 1143 if (argfl || statreq) { 1144 int doacl; 1145 1146 if (lflg) 1147 doacl = 1; 1148 else 1149 doacl = 0; 1150 if ((*statf)(file, &statb) < 0) { 1151 if (argfl || errno != ENOENT || 1152 (Lflg && lstat(file, &statb) == 0)) { 1153 /* 1154 * Avoid race between readdir and lstat. 1155 * Print error message in case of dangling link. 1156 */ 1157 perror(file); 1158 } 1159 nfiles--; 1160 return (NULL); 1161 } 1162 1163 /* 1164 * If -H was specified, and the file linked to was 1165 * not a directory, then we need to get the info 1166 * for the symlink itself. 1167 */ 1168 if ((Hflg) && (argfl) && 1169 ((statb.st_mode & S_IFMT) != S_IFDIR)) { 1170 if (lstat(file, &statb) < 0) { 1171 perror(file); 1172 } 1173 } 1174 1175 rep->lnum = statb.st_ino; 1176 rep->lsize = statb.st_size; 1177 rep->lblocks = statb.st_blocks; 1178 switch (statb.st_mode & S_IFMT) { 1179 case S_IFDIR: 1180 rep->ltype = 'd'; 1181 if (Rflg) { 1182 record_ancestry(file, &statb, rep, 1183 argfl, myparent); 1184 } 1185 break; 1186 case S_IFBLK: 1187 rep->ltype = 'b'; 1188 rep->lsize = (off_t)statb.st_rdev; 1189 break; 1190 case S_IFCHR: 1191 rep->ltype = 'c'; 1192 rep->lsize = (off_t)statb.st_rdev; 1193 break; 1194 case S_IFIFO: 1195 rep->ltype = 'p'; 1196 break; 1197 case S_IFSOCK: 1198 rep->ltype = 's'; 1199 rep->lsize = 0; 1200 break; 1201 case S_IFLNK: 1202 /* symbolic links may not have ACLs, so elide acl() */ 1203 if ((Lflg == 0) || (Hflg == 0) || 1204 ((Hflg) && (!argfl))) { 1205 doacl = 0; 1206 } 1207 rep->ltype = 'l'; 1208 if (lflg) { 1209 cc = readlink(file, buf, BUFSIZ); 1210 if (cc >= 0) { 1211 1212 /* 1213 * follow the symbolic link 1214 * to generate the appropriate 1215 * Fflg marker for the object 1216 * eg, /bin -> /sym/bin/ 1217 */ 1218 if ((Fflg || pflg) && 1219 (stat(file, &statb1) >= 0)) { 1220 switch (statb1.st_mode & 1221 S_IFMT) { 1222 case S_IFDIR: 1223 buf[cc++] = '/'; 1224 break; 1225 case S_IFSOCK: 1226 buf[cc++] = '='; 1227 break; 1228 default: 1229 if ((statb1.st_mode & 1230 ~S_IFMT) & 1231 (S_IXUSR|S_IXGRP| 1232 S_IXOTH)) 1233 buf[cc++] = '*'; 1234 break; 1235 } 1236 } 1237 buf[cc] = '\0'; 1238 rep->flinkto = strdup(buf); 1239 } 1240 break; 1241 } 1242 1243 /* 1244 * ls /sym behaves differently from ls /sym/ 1245 * when /sym is a symbolic link. This is fixed 1246 * when explicit arguments are specified. 1247 */ 1248 1249 #ifdef XPG6 1250 /* Do not follow a symlink when -F is specified */ 1251 if ((!argfl) || (argfl && Fflg) || 1252 (stat(file, &statb1) < 0)) 1253 #else 1254 /* Follow a symlink when -F is specified */ 1255 if (!argfl || stat(file, &statb1) < 0) 1256 #endif /* XPG6 */ 1257 break; 1258 if ((statb1.st_mode & S_IFMT) == S_IFDIR) { 1259 statb = statb1; 1260 rep->ltype = 'd'; 1261 rep->lsize = statb1.st_size; 1262 if (Rflg) { 1263 record_ancestry(file, &statb, rep, 1264 argfl, myparent); 1265 } 1266 } 1267 break; 1268 case S_IFDOOR: 1269 rep->ltype = 'D'; 1270 break; 1271 case S_IFREG: 1272 rep->ltype = '-'; 1273 break; 1274 case S_IFPORT: 1275 rep->ltype = 'P'; 1276 break; 1277 default: 1278 rep->ltype = '?'; 1279 break; 1280 } 1281 rep->lflags = statb.st_mode & ~S_IFMT; 1282 1283 if (!S_ISREG(statb.st_mode)) 1284 rep->lflags |= LS_NOTREG; 1285 1286 /* ACL: check acl entries count */ 1287 if (doacl) { 1288 rep->acl = ' '; 1289 if ((aclcnt = acl(file, GETACLCNT, 0, NULL)) > 1290 MIN_ACL_ENTRIES) { 1291 1292 /* this file has a non-trivial acl */ 1293 1294 rep->acl = '+'; 1295 1296 /* 1297 * For files with non-trivial acls, the 1298 * effective group permissions are the 1299 * intersection of the GROUP_OBJ value and 1300 * the CLASS_OBJ (acl mask) value. Determine 1301 * both the GROUP_OBJ and CLASS_OBJ for this 1302 * file and insert the logical AND of those 1303 * two values in the group permissions field 1304 * of the lflags value for this file. 1305 */ 1306 1307 if ((aclp = (aclent_t *)malloc( 1308 (sizeof (aclent_t)) * aclcnt)) == NULL) { 1309 perror("ls"); 1310 exit(2); 1311 } 1312 1313 if (acl(file, GETACL, aclcnt, aclp) < 0) { 1314 free(aclp); 1315 (void) fprintf(stderr, "ls: "); 1316 perror(file); 1317 nfiles--; 1318 err = 2; 1319 return (NULL); 1320 } 1321 1322 /* 1323 * Until found in acl list, assume maximum 1324 * permissions for both group and mask. (Just 1325 * in case the acl lacks either value for 1326 * some reason.) 1327 */ 1328 groupperm = 07; 1329 mask = 07; 1330 grouppermfound = 0; 1331 maskfound = 0; 1332 for (tp = aclp; aclcnt--; tp++) { 1333 if (tp->a_type == GROUP_OBJ) { 1334 groupperm = tp->a_perm; 1335 grouppermfound = 1; 1336 continue; 1337 } 1338 if (tp->a_type == CLASS_OBJ) { 1339 mask = tp->a_perm; 1340 maskfound = 1; 1341 } 1342 if (grouppermfound && maskfound) 1343 break; 1344 } 1345 1346 free(aclp); 1347 1348 /* reset all the group bits */ 1349 rep->lflags &= ~S_IRWXG; 1350 1351 /* 1352 * Now set them to the logical AND of the 1353 * GROUP_OBJ permissions and the acl mask. 1354 */ 1355 1356 rep->lflags |= (groupperm & mask) << 3; 1357 } 1358 1359 if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1) 1360 rep->acl = '@'; 1361 } else 1362 rep->acl = ' '; 1363 1364 /* mask ISARG and other file-type bits */ 1365 1366 rep->luid = statb.st_uid; 1367 rep->lgid = statb.st_gid; 1368 rep->lnl = statb.st_nlink; 1369 if (uflg) 1370 rep->lmtime = statb.st_atim; 1371 else if (cflg) 1372 rep->lmtime = statb.st_ctim; 1373 else 1374 rep->lmtime = statb.st_mtim; 1375 1376 if (rep->ltype != 'b' && rep->ltype != 'c') 1377 tblocks += rep->lblocks; 1378 } 1379 return (rep); 1380 } 1381 1382 /* 1383 * returns pathname of the form dir/file; 1384 * dir and file are null-terminated strings. 1385 */ 1386 static char * 1387 makename(char *dir, char *file) 1388 { 1389 /* 1390 * PATH_MAX is the maximum length of a path name. 1391 * MAXNAMLEN is the maximum length of any path name component. 1392 * Allocate space for both, plus the '/' in the middle 1393 * and the null character at the end. 1394 * dfile is static as this is returned by makename(). 1395 */ 1396 static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1]; 1397 char *dp, *fp; 1398 1399 dp = dfile; 1400 fp = dir; 1401 while (*fp) 1402 *dp++ = *fp++; 1403 if (dp > dfile && *(dp - 1) != '/') 1404 *dp++ = '/'; 1405 fp = file; 1406 while (*fp) 1407 *dp++ = *fp++; 1408 *dp = '\0'; 1409 return (dfile); 1410 } 1411 1412 1413 #include <pwd.h> 1414 #include <grp.h> 1415 #include <utmpx.h> 1416 1417 struct utmpx utmp; 1418 1419 #define NMAX (sizeof (utmp.ut_name)) 1420 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 1421 1422 1423 struct cachenode { /* this struct must be zeroed before using */ 1424 struct cachenode *lesschild; /* subtree whose entries < val */ 1425 struct cachenode *grtrchild; /* subtree whose entries > val */ 1426 long val; /* the uid or gid of this entry */ 1427 int initted; /* name has been filled in */ 1428 char name[NMAX+1]; /* the string that val maps to */ 1429 }; 1430 static struct cachenode *names, *groups; 1431 1432 static struct cachenode * 1433 findincache(struct cachenode **head, long val) 1434 { 1435 struct cachenode **parent = head; 1436 struct cachenode *c = *parent; 1437 1438 while (c != NULL) { 1439 if (val == c->val) { 1440 /* found it */ 1441 return (c); 1442 } else if (val < c->val) { 1443 parent = &c->lesschild; 1444 c = c->lesschild; 1445 } else { 1446 parent = &c->grtrchild; 1447 c = c->grtrchild; 1448 } 1449 } 1450 1451 /* not in the cache, make a new entry for it */ 1452 c = calloc(1, sizeof (struct cachenode)); 1453 if (c == NULL) { 1454 perror("ls"); 1455 exit(2); 1456 } 1457 *parent = c; 1458 c->val = val; 1459 return (c); 1460 } 1461 1462 /* 1463 * get name from cache, or passwd file for a given uid; 1464 * lastuid is set to uid. 1465 */ 1466 static char * 1467 getname(uid_t uid) 1468 { 1469 struct passwd *pwent; 1470 struct cachenode *c; 1471 1472 if ((uid == lastuid) && lastuname) 1473 return (lastuname); 1474 1475 c = findincache(&names, uid); 1476 if (c->initted == 0) { 1477 if ((pwent = getpwuid(uid)) != NULL) { 1478 SCPYN(&c->name[0], pwent->pw_name); 1479 } else { 1480 (void) sprintf(&c->name[0], "%-8u", (int)uid); 1481 } 1482 c->initted = 1; 1483 } 1484 lastuid = uid; 1485 lastuname = &c->name[0]; 1486 return (lastuname); 1487 } 1488 1489 /* 1490 * get name from cache, or group file for a given gid; 1491 * lastgid is set to gid. 1492 */ 1493 static char * 1494 getgroup(gid_t gid) 1495 { 1496 struct group *grent; 1497 struct cachenode *c; 1498 1499 if ((gid == lastgid) && lastgname) 1500 return (lastgname); 1501 1502 c = findincache(&groups, gid); 1503 if (c->initted == 0) { 1504 if ((grent = getgrgid(gid)) != NULL) { 1505 SCPYN(&c->name[0], grent->gr_name); 1506 } else { 1507 (void) sprintf(&c->name[0], "%-8u", (int)gid); 1508 } 1509 c->initted = 1; 1510 } 1511 lastgid = gid; 1512 lastgname = &c->name[0]; 1513 return (lastgname); 1514 } 1515 1516 /* return >0 if item pointed by pp2 should appear first */ 1517 static int 1518 compar(struct lbuf **pp1, struct lbuf **pp2) 1519 { 1520 struct lbuf *p1, *p2; 1521 1522 p1 = *pp1; 1523 p2 = *pp2; 1524 if (dflg == 0) { 1525 /* 1526 * compare two names in ls-command one of which is file 1527 * and the other is a directory; 1528 * this portion is not used for comparing files within 1529 * a directory name of ls-command; 1530 */ 1531 if (p1->lflags&ISARG && p1->ltype == 'd') { 1532 if (!(p2->lflags&ISARG && p2->ltype == 'd')) 1533 return (1); 1534 } else { 1535 if (p2->lflags&ISARG && p2->ltype == 'd') 1536 return (-1); 1537 } 1538 } 1539 if (tflg) { 1540 if (p2->lmtime.tv_sec > p1->lmtime.tv_sec) 1541 return (rflg); 1542 else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec) 1543 return (-rflg); 1544 /* times are equal to the sec, check nsec */ 1545 if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec) 1546 return (rflg); 1547 else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec) 1548 return (-rflg); 1549 /* if times are equal, fall through and sort by name */ 1550 } else if (Sflg) { 1551 /* 1552 * The size stored in lsize can be either the 1553 * size or the major minor number (in the case of 1554 * block and character special devices). If it's 1555 * a major minor number, then the size is considered 1556 * to be zero and we want to fall through and sort 1557 * by name. In addition, if the size of p2 is equal 1558 * to the size of p1 we want to fall through and 1559 * sort by name. 1560 */ 1561 off_t p1size = (p1->ltype == 'b') || 1562 (p1->ltype == 'c') ? 0 : p1->lsize; 1563 off_t p2size = (p2->ltype == 'b') || 1564 (p2->ltype == 'c') ? 0 : p2->lsize; 1565 if (p2size > p1size) { 1566 return (rflg); 1567 } else if (p2size < p1size) { 1568 return (-rflg); 1569 } 1570 /* Sizes are equal, fall through and sort by name. */ 1571 } 1572 return (rflg * strcoll( 1573 p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname, 1574 p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname)); 1575 } 1576 1577 static void 1578 pprintf(char *s1, char *s2) 1579 { 1580 csi_pprintf((unsigned char *)s1); 1581 csi_pprintf((unsigned char *)s2); 1582 } 1583 1584 static void 1585 csi_pprintf(unsigned char *s) 1586 { 1587 unsigned char *cp; 1588 char c; 1589 int i; 1590 int c_len; 1591 int p_col; 1592 wchar_t pcode; 1593 1594 if (!qflg && !bflg) { 1595 for (cp = s; *cp != '\0'; cp++) { 1596 (void) putchar(*cp); 1597 curcol++; 1598 } 1599 return; 1600 } 1601 1602 for (cp = s; *cp; ) { 1603 if (isascii(c = *cp)) { 1604 if (!isprint(c)) { 1605 if (qflg) { 1606 c = '?'; 1607 } else { 1608 curcol += 3; 1609 (void) putc('\\', stdout); 1610 c = '0' + ((*cp >> 6) & 07); 1611 (void) putc(c, stdout); 1612 c = '0' + ((*cp >> 3) & 07); 1613 (void) putc(c, stdout); 1614 c = '0' + (*cp & 07); 1615 } 1616 } 1617 curcol++; 1618 cp++; 1619 (void) putc(c, stdout); 1620 continue; 1621 } 1622 1623 if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) { 1624 c_len = 1; 1625 goto not_print; 1626 } 1627 1628 if ((p_col = wcwidth(pcode)) > 0) { 1629 (void) putwchar(pcode); 1630 cp += c_len; 1631 curcol += p_col; 1632 continue; 1633 } 1634 1635 not_print: 1636 for (i = 0; i < c_len; i++) { 1637 if (qflg) { 1638 c = '?'; 1639 } else { 1640 curcol += 3; 1641 (void) putc('\\', stdout); 1642 c = '0' + ((*cp >> 6) & 07); 1643 (void) putc(c, stdout); 1644 c = '0' + ((*cp >> 3) & 07); 1645 (void) putc(c, stdout); 1646 c = '0' + (*cp & 07); 1647 } 1648 curcol++; 1649 (void) putc(c, stdout); 1650 cp++; 1651 } 1652 } 1653 } 1654 1655 static int 1656 strcol(unsigned char *s1) 1657 { 1658 int w; 1659 int w_col; 1660 int len; 1661 wchar_t wc; 1662 1663 w = 0; 1664 while (*s1) { 1665 if (isascii(*s1)) { 1666 w++; 1667 s1++; 1668 continue; 1669 } 1670 1671 if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) { 1672 w++; 1673 s1++; 1674 continue; 1675 } 1676 1677 if ((w_col = wcwidth(wc)) < 0) 1678 w_col = len; 1679 s1 += len; 1680 w += w_col; 1681 } 1682 return (w); 1683 } 1684 1685 /* 1686 * Convert an unsigned long long to a string representation and place the 1687 * result in the caller-supplied buffer. 1688 * 1689 * The number provided is a size in bytes. The number is first 1690 * converted to an integral multiple of 'scale' bytes. This new 1691 * number is then scaled down until it is small enough to be in a good 1692 * human readable format, i.e. in the range 0 thru scale-1. If the 1693 * number used to derive the final number is not a multiple of scale, and 1694 * the final number has only a single significant digit, we compute 1695 * tenths of units to provide a second significant digit. 1696 * 1697 * The value "(unsigned long long)-1" is a special case and is always 1698 * converted to "-1". 1699 * 1700 * A pointer to the caller-supplied buffer is returned. 1701 */ 1702 static char * 1703 number_to_scaled_string( 1704 numbuf_t buf, /* put the result here */ 1705 unsigned long long number, /* convert this number */ 1706 long scale) 1707 { 1708 unsigned long long save; 1709 /* Measurement: kilo, mega, giga, tera, peta, exa */ 1710 char *uom = "KMGTPE"; 1711 1712 if ((long long)number == (long long)-1) { 1713 (void) strlcpy(buf, "-1", sizeof (numbuf_t)); 1714 return (buf); 1715 } 1716 1717 save = number; 1718 number = number / scale; 1719 1720 /* 1721 * Now we have number as a count of scale units. 1722 * If no further scaling is necessary, we round up as appropriate. 1723 * 1724 * The largest value number could have had entering the routine is 1725 * 16 Exabytes, so running off the end of the uom array should 1726 * never happen. We check for that, though, as a guard against 1727 * a breakdown elsewhere in the algorithm. 1728 */ 1729 if (number < (unsigned long long)scale) { 1730 if ((save % scale) >= (unsigned long long)(scale / 2)) { 1731 if (++number == (unsigned long long)scale) { 1732 uom++; 1733 number = 1; 1734 } 1735 } 1736 } else { 1737 while ((number >= (unsigned long long)scale) && (*uom != 'E')) { 1738 uom++; /* next unit of measurement */ 1739 save = number; 1740 /* 1741 * If we're over half way to the next unit of 1742 * 'scale' bytes (which means we should round 1743 * up), then adding half of 'scale' prior to 1744 * the division will push us into that next 1745 * unit of scale when we perform the division 1746 */ 1747 number = (number + (scale / 2)) / scale; 1748 } 1749 } 1750 1751 /* check if we should output a decimal place after the point */ 1752 if ((save / scale) < 10) { 1753 /* snprintf() will round for us */ 1754 float fnum = (float)save / scale; 1755 (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c", 1756 fnum, *uom); 1757 } else { 1758 (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c", 1759 number, *uom); 1760 } 1761 return (buf); 1762 } 1763