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