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