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