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 /* 23 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2012, Joyent, Inc. All rights reserved. 25 */ 26 27 /* 28 * Copyright 2009 Jason King. All rights reserved. 29 * Use is subject to license terms. 30 */ 31 32 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 33 /* All Rights Reserved */ 34 35 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 36 /* All Rights Reserved */ 37 38 /* 39 * List files or directories 40 */ 41 42 #include <sys/param.h> 43 #include <sys/types.h> 44 #include <sys/mkdev.h> 45 #include <sys/stat.h> 46 #include <sys/acl.h> 47 48 #include <wchar.h> 49 #include <stdio.h> 50 #include <ctype.h> 51 #include <dirent.h> 52 #include <string.h> 53 #include <locale.h> 54 #include <curses.h> 55 #include <term.h> 56 #include <termios.h> 57 #include <stdlib.h> 58 #include <widec.h> 59 #include <locale.h> 60 #include <wctype.h> 61 #include <pwd.h> 62 #include <grp.h> 63 #include <limits.h> 64 #include <fcntl.h> 65 #include <unistd.h> 66 #include <libgen.h> 67 #include <errno.h> 68 #include <aclutils.h> 69 #include <libnvpair.h> 70 #include <libcmdutils.h> 71 #include <attr.h> 72 #include <getopt.h> 73 #include <inttypes.h> 74 75 #ifndef STANDALONE 76 #define TERMINFO 77 #endif 78 79 /* 80 * -DNOTERMINFO can be defined on the cc command line to prevent 81 * the use of terminfo. This should be done on systems not having 82 * the terminfo feature(pre 6.0 systems ?). 83 * As a result, columnar listings assume 80 columns for output, 84 * unless told otherwise via the COLUMNS environment variable. 85 */ 86 #ifdef NOTERMINFO 87 #undef TERMINFO 88 #endif 89 90 #include <term.h> 91 92 #define BFSIZE 16 93 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */ 94 #define ISARG 0100000 95 96 /* 97 * this flag has been added to manipulate the display of S instead of 'l' when 98 * the file is not a regular file and when group execution bit is off 99 */ 100 #define LS_NOTREG 010000 101 102 103 /* 104 * Date and time formats 105 * 106 * b --- abbreviated month name 107 * e --- day number 108 * Y --- year in the form ccyy 109 * H --- hour(24-hour version) 110 * M --- minute 111 * F --- yyyy-mm-dd 112 * T --- hh:mm:ss 113 * z --- time zone as hours displacement from UTC 114 * note that %F and %z are from the ISO C99 standard and are 115 * not present in older C libraries 116 */ 117 #define FORMAT_OLD " %b %e %Y " 118 #define FORMAT_NEW " %b %e %H:%M " 119 #define FORMAT_LONG " %b %e %T %Y " 120 #define FORMAT_ISO_FULL " %%F %%T.%.09ld %%z " 121 #define FORMAT_ISO_LONG " %F %R " 122 #define FORMAT_ISO_NEW " %m-%d %H:%M " 123 #define FORMAT_ISO_OLD " %F " 124 125 #undef BUFSIZ 126 #define BUFSIZ 4096 127 #define NUMBER_WIDTH 40 128 #define FMTSIZE 50 129 130 struct ditem { 131 dev_t dev; /* directory items device number */ 132 ino_t ino; /* directory items inode number */ 133 struct ditem *parent; /* dir items ptr to its parent's info */ 134 }; 135 /* Holds boolean extended system attributes */ 136 struct attrb { 137 char *name; 138 }; 139 /* Holds timestamp extended system attributes */ 140 struct attrtm { 141 char *name; 142 uint64_t stm; 143 uint64_t nstm; 144 }; 145 146 #define LSA_NONE (0) 147 #define LSA_BOLD (1L << 0) 148 #define LSA_UNDERSCORE (1L << 1) 149 #define LSA_BLINK (1L << 2) 150 #define LSA_REVERSE (1L << 3) 151 #define LSA_CONCEALED (1L << 4) 152 153 /* these should be ordered most general to most specific */ 154 typedef enum LS_CFTYPE { 155 LS_NORMAL, 156 LS_FILE, 157 LS_EXEC, 158 LS_DIR, 159 LS_LINK, 160 LS_FIFO, 161 LS_SOCK, 162 LS_DOOR, 163 LS_BLK, 164 LS_CHR, 165 LS_PORT, 166 LS_STICKY, 167 LS_ORPHAN, 168 LS_SETGID, 169 LS_SETUID, 170 LS_OTHER_WRITABLE, 171 LS_STICKY_OTHER_WRITABLE, 172 LS_PAT 173 } ls_cftype_t; 174 175 typedef struct { 176 char *sfx; 177 ls_cftype_t ftype; 178 int attr; 179 int fg; 180 int bg; 181 } ls_color_t; 182 183 struct lbuf { 184 union { 185 char lname[MAXNAMLEN]; /* used for filename in a directory */ 186 char *namep; /* for name in ls-command; */ 187 } ln; 188 char ltype; /* filetype */ 189 ino_t lnum; /* inode number of file */ 190 mode_t lflags; /* 0777 bits used as r,w,x permissions */ 191 nlink_t lnl; /* number of links to file */ 192 uid_t luid; 193 gid_t lgid; 194 off_t lsize; /* filesize or major/minor dev numbers */ 195 blkcnt_t lblocks; /* number of file blocks */ 196 timestruc_t lmtime; 197 timestruc_t lat; 198 timestruc_t lct; 199 timestruc_t lmt; 200 char *flinkto; /* symbolic link contents */ 201 char acl; /* indicate there are additional acl entries */ 202 int cycle; /* cycle detected flag */ 203 struct ditem *ancinfo; /* maintains ancestor info */ 204 acl_t *aclp; /* ACL if present */ 205 struct attrb *exttr; /* boolean extended system attributes */ 206 struct attrtm *extm; /* timestamp extended system attributes */ 207 ls_color_t *color; /* color for entry */ 208 ls_color_t *link_color; /* color for symlink */ 209 }; 210 211 struct dchain { 212 char *dc_name; /* path name */ 213 int cycle_detected; /* cycle detected visiting this directory */ 214 struct ditem *myancinfo; /* this directory's ancestry info */ 215 struct dchain *dc_next; /* next directory in the chain */ 216 }; 217 218 /* 219 * A numbuf_t is used when converting a number to a string representation 220 */ 221 typedef char numbuf_t[NUMBER_WIDTH]; 222 223 static struct dchain *dfirst; /* start of the dir chain */ 224 static struct dchain *cdfirst; /* start of the current dir chain */ 225 static struct dchain *dtemp; /* temporary - used for linking */ 226 static char *curdir; /* the current directory */ 227 228 static int first = 1; /* true if first line is not yet printed */ 229 static int nfiles = 0; /* number of flist entries in current use */ 230 static int nargs = 0; /* number of flist entries used for arguments */ 231 static int maxfils = 0; /* number of flist/lbuf entries allocated */ 232 static int maxn = 0; /* number of flist entries with lbufs asigned */ 233 static int quantn = 64; /* allocation growth quantum */ 234 235 static struct lbuf *nxtlbf; /* ptr to next lbuf to be assigned */ 236 static struct lbuf **flist; /* ptr to list of lbuf pointers */ 237 static struct lbuf *gstat(char *, int, struct ditem *); 238 static char *getname(uid_t); 239 static char *getgroup(gid_t); 240 static char *makename(char *, char *); 241 static void pentry(struct lbuf *); 242 static void column(void); 243 static void pmode(mode_t aflag); 244 static void selection(int *); 245 static void new_line(void); 246 static void rddir(char *, struct ditem *); 247 static int strcol(unsigned char *); 248 static void pem(struct lbuf **, struct lbuf **, int); 249 static void pdirectory(char *, int, int, int, struct ditem *); 250 static struct cachenode *findincache(struct cachenode **, long); 251 static void csi_pprintf(unsigned char *); 252 static void pprintf(char *, char *); 253 static int compar(struct lbuf **pp1, struct lbuf **pp2); 254 static char *number_to_scaled_string(numbuf_t buf, 255 unsigned long long number, 256 long scale); 257 static void record_ancestry(char *, struct stat *, struct lbuf *, 258 int, struct ditem *); 259 static void ls_color_init(void); 260 static ls_color_t *ls_color_find(const char *, mode_t); 261 static void ls_start_color(ls_color_t *); 262 static void ls_end_color(void); 263 264 static int aflg; 265 static int atflg; 266 static int bflg; 267 static int cflg; 268 static int dflg; 269 static int eflg; 270 static int fflg; 271 static int gflg; 272 static int hflg; 273 static int iflg; 274 static int lflg; 275 static int mflg; 276 static int nflg; 277 static int oflg; 278 static int pflg; 279 static int qflg; 280 static int rflg = 1; /* init to 1 for special use in compar */ 281 static int sflg; 282 static int tflg; 283 static int uflg; 284 static int Uflg; 285 static int wflg; 286 static int xflg; 287 static int Aflg; 288 static int Bflg; 289 static int Cflg; 290 static int Eflg; 291 static int Fflg; 292 static int Hflg; 293 static int Lflg; 294 static int Rflg; 295 static int Sflg; 296 static int vflg; 297 static int Vflg; 298 static int saflg; /* boolean extended system attr. */ 299 static int sacnt; /* number of extended system attr. */ 300 static int copt; 301 static int vopt; 302 static int tmflg; /* create time ext. system attr. */ 303 static int ctm; 304 static int atm; 305 static int mtm; 306 static int crtm; 307 static int alltm; 308 static long hscale; 309 static mode_t flags; 310 static int err = 0; /* Contains return code */ 311 static int colorflg; 312 static int file_typeflg; 313 static int noflist = 0; 314 315 static uid_t lastuid = (uid_t)-1; 316 static gid_t lastgid = (gid_t)-1; 317 static char *lastuname = NULL; 318 static char *lastgname = NULL; 319 320 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */ 321 static int statreq; 322 323 static uint64_t block_size = 1; 324 static char *dotp = "."; 325 326 static u_longlong_t tblocks; /* number of blocks of files in a directory */ 327 static time_t year, now; 328 329 static int num_cols = 80; 330 static int colwidth; 331 static int filewidth; 332 static int fixedwidth; 333 static int nomocore; 334 static int curcol; 335 336 static struct winsize win; 337 338 /* if time_fmt_new is left NULL, time_fmt_old is used for all times */ 339 static const char *time_fmt_old = FORMAT_OLD; /* non-recent files */ 340 static const char *time_fmt_new = FORMAT_NEW; /* recent files */ 341 static int time_custom; /* != 0 if a custom format */ 342 static char time_buf[FMTSIZE]; /* array to hold day and time */ 343 344 static int lsc_debug; 345 static ls_color_t *lsc_match; 346 static ls_color_t *lsc_colors; 347 static size_t lsc_ncolors; 348 static char *lsc_bold; 349 static char *lsc_underline; 350 static char *lsc_blink; 351 static char *lsc_reverse; 352 static char *lsc_concealed; 353 static char *lsc_none; 354 static char *lsc_setfg; 355 static char *lsc_setbg; 356 static ls_color_t *lsc_orphan; 357 358 #define NOTWORKINGDIR(d, l) (((l) < 2) || \ 359 (strcmp((d) + (l) - 2, "/.") != 0)) 360 361 #define NOTPARENTDIR(d, l) (((l) < 3) || \ 362 (strcmp((d) + (l) - 3, "/..") != 0)) 363 /* Extended system attributes support */ 364 static int get_sysxattr(char *, struct lbuf *); 365 static void set_sysattrb_display(char *, boolean_t, struct lbuf *); 366 static void set_sysattrtm_display(char *, struct lbuf *); 367 static void format_time(time_t, time_t); 368 static void print_time(struct lbuf *); 369 static void format_attrtime(struct lbuf *); 370 static void *xmalloc(size_t, struct lbuf *); 371 static void free_sysattr(struct lbuf *); 372 static nvpair_t *pair; 373 static nvlist_t *response; 374 static int acl_err; 375 376 const struct option long_options[] = { 377 { "all", no_argument, NULL, 'a' }, 378 { "almost-all", no_argument, NULL, 'A' }, 379 { "escape", no_argument, NULL, 'b' }, 380 { "classify", no_argument, NULL, 'F' }, 381 { "human-readable", no_argument, NULL, 'h' }, 382 { "dereference", no_argument, NULL, 'L' }, 383 { "dereference-command-line", no_argument, NULL, 'H' }, 384 { "ignore-backups", no_argument, NULL, 'B' }, 385 { "inode", no_argument, NULL, 'i' }, 386 { "numeric-uid-gid", no_argument, NULL, 'n' }, 387 { "no-group", no_argument, NULL, 'o' }, 388 { "hide-control-chars", no_argument, NULL, 'q' }, 389 { "reverse", no_argument, NULL, 'r' }, 390 { "recursive", no_argument, NULL, 'R' }, 391 { "size", no_argument, NULL, 's' }, 392 { "width", required_argument, NULL, 'w' }, 393 394 /* no short options for these */ 395 { "block-size", required_argument, NULL, 0 }, 396 { "full-time", no_argument, NULL, 0 }, 397 { "si", no_argument, NULL, 0 }, 398 { "color", optional_argument, NULL, 0 }, 399 { "colour", optional_argument, NULL, 0}, 400 { "file-type", no_argument, NULL, 0 }, 401 { "time-style", required_argument, NULL, 0 }, 402 403 {0, 0, 0, 0} 404 }; 405 406 int 407 main(int argc, char *argv[]) 408 { 409 int c; 410 int i; 411 int width; 412 int amino = 0; 413 int opterr = 0; 414 int option_index = 0; 415 struct lbuf *ep; 416 struct lbuf lb; 417 struct ditem *myinfo = NULL; 418 419 (void) setlocale(LC_ALL, ""); 420 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 421 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 422 #endif 423 (void) textdomain(TEXT_DOMAIN); 424 #ifdef STANDALONE 425 if (argv[0][0] == '\0') 426 argc = getargv("ls", &argv, 0); 427 #endif 428 429 lb.lmtime.tv_sec = time(NULL); 430 lb.lmtime.tv_nsec = 0; 431 year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */ 432 now = lb.lmtime.tv_sec + 60; 433 if (isatty(1)) { 434 Cflg = 1; 435 mflg = 0; 436 } 437 438 while ((c = getopt_long(argc, argv, 439 "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options, 440 &option_index)) != -1) 441 switch (c) { 442 case 0: 443 /* non-short options */ 444 if (strcmp(long_options[option_index].name, 445 "color") == 0 || 446 strcmp(long_options[option_index].name, 447 "colour") == 0) { 448 if (optarg == NULL || 449 strcmp(optarg, "always") == 0 || 450 strcmp(optarg, "yes") == 0 || 451 strcmp(optarg, "force") == 0) { 452 colorflg++; 453 statreq++; 454 continue; 455 } 456 457 if (strcmp(optarg, "auto") == 0 || 458 strcmp(optarg, "tty") == 0 || 459 strcmp(optarg, "if-tty") == 0) { 460 if (isatty(1) == 1) { 461 colorflg++; 462 statreq++; 463 } 464 continue; 465 } 466 467 if (strcmp(optarg, "never") == 0 || 468 strcmp(optarg, "no") == 0 || 469 strcmp(optarg, "none") == 0) { 470 colorflg = 0; 471 continue; 472 } 473 (void) fprintf(stderr, 474 gettext("Invalid argument '%s' for " 475 "--color\n"), optarg); 476 ++opterr; 477 continue; 478 } 479 480 if (strcmp(long_options[option_index].name, 481 "si") == 0) { 482 hflg++; 483 hscale = 1000; 484 continue; 485 } 486 487 if (strcmp(long_options[option_index].name, 488 "block-size") == 0) { 489 size_t scale_len = strlen(optarg); 490 uint64_t scale = 1; 491 uint64_t kilo = 1024; 492 char scale_c; 493 494 if (scale_len == 0) { 495 (void) fprintf(stderr, gettext( 496 "Invalid block size \'%s\'\n"), 497 optarg); 498 exit(1); 499 } 500 501 scale_c = optarg[scale_len - 1]; 502 if (scale_c == 'B') { 503 /* need at least digit, scale, B */ 504 if (scale_len < 3) { 505 (void) fprintf(stderr, gettext( 506 "Invalid block size " 507 "\'%s\'\n"), optarg); 508 exit(1); 509 } 510 kilo = 1000; 511 scale_c = optarg[scale_len - 2]; 512 if (isdigit(scale_c)) { 513 (void) fprintf(stderr, 514 gettext("Invalid block size" 515 " \'%s\'\n"), optarg); 516 exit(1); 517 } 518 /* 519 * make optarg[scale_len - 1] point to 520 * the scale factor 521 */ 522 --scale_len; 523 } 524 525 switch (scale_c) { 526 case 'y': 527 case 'Y': 528 scale *= kilo; 529 /*FALLTHROUGH*/ 530 case 'Z': 531 case 'z': 532 scale *= kilo; 533 /*FALLTHROUGH*/ 534 case 'E': 535 case 'e': 536 scale *= kilo; 537 /*FALLTHROUGH*/ 538 case 'P': 539 case 'p': 540 scale *= kilo; 541 /*FALLTHROUGH*/ 542 case 'T': 543 case 't': 544 scale *= kilo; 545 /*FALLTHROUGH*/ 546 case 'G': 547 case 'g': 548 scale *= kilo; 549 /*FALLTHROUGH*/ 550 case 'M': 551 case 'm': 552 scale *= kilo; 553 /*FALLTHROUGH*/ 554 case 'K': 555 case 'k': 556 scale *= kilo; 557 break; 558 default: 559 if (!isdigit(scale_c)) { 560 (void) fprintf(stderr, 561 gettext("Invalid character " 562 "following block size in " 563 "\'%s\'\n"), optarg); 564 exit(1); 565 } 566 } 567 568 /* NULL out scale constant if present */ 569 if (scale > 1 && !isdigit(scale_c)) 570 optarg[scale_len - 1] = '\0'; 571 572 /* Based on testing, this is what GNU ls does */ 573 block_size = strtoll(optarg, NULL, 0) * scale; 574 if (block_size < 1) { 575 (void) fprintf(stderr, 576 gettext("Invalid block size " 577 "\'%s\'\n"), optarg); 578 exit(1); 579 } 580 continue; 581 } 582 583 if (strcmp(long_options[option_index].name, 584 "file-type") == 0) { 585 file_typeflg++; 586 Fflg++; 587 statreq++; 588 continue; 589 } 590 591 592 if (strcmp(long_options[option_index].name, 593 "full-time") == 0) { 594 Eflg++; 595 statreq++; 596 eflg = 0; 597 time_fmt_old = FORMAT_ISO_FULL; 598 time_fmt_new = FORMAT_ISO_FULL; 599 continue; 600 } 601 602 if (strcmp(long_options[option_index].name, 603 "time-style") == 0) { 604 /* like -E, but doesn't imply -l */ 605 if (strcmp(optarg, "full-iso") == 0) { 606 Eflg++; 607 statreq++; 608 eflg = 0; 609 time_fmt_old = FORMAT_ISO_FULL; 610 time_fmt_new = FORMAT_ISO_FULL; 611 continue; 612 } 613 if (strcmp(optarg, "long-iso") == 0) { 614 statreq++; 615 Eflg = 0; 616 eflg = 0; 617 time_fmt_old = FORMAT_ISO_LONG; 618 time_fmt_new = FORMAT_ISO_LONG; 619 continue; 620 } 621 if (strcmp(optarg, "iso") == 0) { 622 statreq++; 623 Eflg = 0; 624 eflg = 0; 625 time_fmt_old = FORMAT_ISO_OLD; 626 time_fmt_new = FORMAT_ISO_NEW; 627 continue; 628 } 629 /* should be the default */ 630 if (strcmp(optarg, "locale") == 0) { 631 time_fmt_old = FORMAT_OLD; 632 time_fmt_new = FORMAT_NEW; 633 continue; 634 } 635 if (optarg[0] == '+') { 636 char *told, *tnew; 637 char *p; 638 size_t timelen = strlen(optarg); 639 640 p = strchr(optarg, '\n'); 641 if (p != NULL) 642 *p++ = '\0'; 643 644 /* 645 * Time format requires a leading and 646 * trailing space 647 * Add room for 3 spaces + 2 nulls 648 * The + in optarg is replaced with 649 * a space. 650 */ 651 timelen += 2 + 3; 652 told = malloc(timelen); 653 if (told == NULL) { 654 perror("ls"); 655 exit(2); 656 } 657 658 (void) memset(told, 0, timelen); 659 told[0] = ' '; 660 (void) strlcat(told, &optarg[1], 661 timelen); 662 (void) strlcat(told, " ", timelen); 663 664 if (p != NULL) { 665 size_t tnew_len; 666 667 tnew = told + strlen(told) + 1; 668 tnew_len = timelen - 669 strlen(told) - 1; 670 671 tnew[0] = ' '; 672 (void) strlcat(tnew, p, 673 tnew_len); 674 (void) strlcat(tnew, " ", 675 tnew_len); 676 time_fmt_new = 677 (const char *)tnew; 678 } else { 679 time_fmt_new = 680 (const char *)told; 681 } 682 683 time_fmt_old = (const char *)told; 684 time_custom = 1; 685 continue; 686 } 687 continue; 688 } 689 690 continue; 691 692 case 'a': 693 aflg++; 694 continue; 695 case 'A': 696 Aflg++; 697 continue; 698 case 'b': 699 bflg = 1; 700 qflg = 0; 701 continue; 702 case 'B': 703 Bflg = 1; 704 continue; 705 case 'c': 706 uflg = 0; 707 atm = 0; 708 ctm = 0; 709 mtm = 0; 710 crtm = 0; 711 cflg++; 712 continue; 713 case 'C': 714 Cflg = 1; 715 mflg = 0; 716 #ifdef XPG4 717 lflg = 0; 718 #endif 719 continue; 720 case 'd': 721 dflg++; 722 continue; 723 case 'e': 724 eflg++; 725 lflg++; 726 statreq++; 727 Eflg = 0; 728 time_fmt_old = FORMAT_LONG; 729 time_fmt_new = FORMAT_LONG; 730 continue; 731 case 'E': 732 Eflg++; 733 lflg++; 734 statreq++; 735 eflg = 0; 736 time_fmt_old = FORMAT_ISO_FULL; 737 time_fmt_new = FORMAT_ISO_FULL; 738 continue; 739 case 'f': 740 fflg++; 741 continue; 742 case 'F': 743 Fflg++; 744 statreq++; 745 continue; 746 case 'g': 747 gflg++; 748 lflg++; 749 statreq++; 750 continue; 751 case 'h': 752 hflg++; 753 hscale = 1024; 754 continue; 755 case 'H': 756 Hflg++; 757 /* -H and -L are mutually exclusive */ 758 Lflg = 0; 759 continue; 760 case 'i': 761 iflg++; 762 continue; 763 case 'k': 764 block_size = 1024; 765 continue; 766 case 'l': 767 lflg++; 768 statreq++; 769 Cflg = 0; 770 xflg = 0; 771 mflg = 0; 772 atflg = 0; 773 continue; 774 case 'L': 775 Lflg++; 776 /* -H and -L are mutually exclusive */ 777 Hflg = 0; 778 continue; 779 case 'm': 780 Cflg = 0; 781 mflg = 1; 782 #ifdef XPG4 783 lflg = 0; 784 #endif 785 continue; 786 case 'n': 787 nflg++; 788 lflg++; 789 statreq++; 790 Cflg = 0; 791 xflg = 0; 792 mflg = 0; 793 atflg = 0; 794 continue; 795 case 'o': 796 oflg++; 797 lflg++; 798 statreq++; 799 continue; 800 case 'p': 801 pflg++; 802 statreq++; 803 continue; 804 case 'q': 805 qflg = 1; 806 bflg = 0; 807 continue; 808 case 'r': 809 rflg = -1; 810 continue; 811 case 'R': 812 Rflg++; 813 statreq++; 814 continue; 815 case 's': 816 sflg++; 817 statreq++; 818 continue; 819 case 'S': 820 tflg = 0; 821 Uflg = 0; 822 Sflg++; 823 statreq++; 824 continue; 825 case 't': 826 Sflg = 0; 827 Uflg = 0; 828 tflg++; 829 statreq++; 830 continue; 831 case 'U': 832 Sflg = 0; 833 tflg = 0; 834 Uflg++; 835 continue; 836 case 'u': 837 cflg = 0; 838 atm = 0; 839 ctm = 0; 840 mtm = 0; 841 crtm = 0; 842 uflg++; 843 continue; 844 case 'V': 845 Vflg++; 846 /*FALLTHROUGH*/ 847 case 'v': 848 vflg++; 849 #if !defined(XPG4) 850 if (lflg) 851 continue; 852 #endif 853 lflg++; 854 statreq++; 855 Cflg = 0; 856 xflg = 0; 857 mflg = 0; 858 continue; 859 case 'w': 860 wflg++; 861 num_cols = atoi(optarg); 862 continue; 863 case 'x': 864 xflg = 1; 865 Cflg = 1; 866 mflg = 0; 867 #ifdef XPG4 868 lflg = 0; 869 #endif 870 continue; 871 case '1': 872 Cflg = 0; 873 continue; 874 case '@': 875 #if !defined(XPG4) 876 /* 877 * -l has precedence over -@ 878 */ 879 if (lflg) 880 continue; 881 #endif 882 atflg++; 883 lflg++; 884 statreq++; 885 Cflg = 0; 886 xflg = 0; 887 mflg = 0; 888 continue; 889 case '/': 890 saflg++; 891 if (optarg != NULL) { 892 if (strcmp(optarg, "c") == 0) { 893 copt++; 894 vopt = 0; 895 } else if (strcmp(optarg, "v") == 0) { 896 vopt++; 897 copt = 0; 898 } else 899 opterr++; 900 } else 901 opterr++; 902 lflg++; 903 statreq++; 904 Cflg = 0; 905 xflg = 0; 906 mflg = 0; 907 continue; 908 case '%': 909 tmflg++; 910 if (optarg != NULL) { 911 if (strcmp(optarg, "ctime") == 0) { 912 ctm++; 913 atm = 0; 914 mtm = 0; 915 crtm = 0; 916 } else if (strcmp(optarg, "atime") == 0) { 917 atm++; 918 ctm = 0; 919 mtm = 0; 920 crtm = 0; 921 uflg = 0; 922 cflg = 0; 923 } else if (strcmp(optarg, "mtime") == 0) { 924 mtm++; 925 atm = 0; 926 ctm = 0; 927 crtm = 0; 928 uflg = 0; 929 cflg = 0; 930 } else if (strcmp(optarg, "crtime") == 0) { 931 crtm++; 932 atm = 0; 933 ctm = 0; 934 mtm = 0; 935 uflg = 0; 936 cflg = 0; 937 } else if (strcmp(optarg, "all") == 0) { 938 alltm++; 939 atm = 0; 940 ctm = 0; 941 mtm = 0; 942 crtm = 0; 943 } else 944 opterr++; 945 } else 946 opterr++; 947 948 Sflg = 0; 949 statreq++; 950 mflg = 0; 951 continue; 952 case '?': 953 opterr++; 954 continue; 955 } 956 957 if (opterr) { 958 (void) fprintf(stderr, gettext( 959 "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]" 960 "%%[atime | crtime | ctime | mtime | all]" 961 " [files]\n")); 962 exit(2); 963 } 964 965 if (fflg) { 966 aflg++; 967 lflg = 0; 968 sflg = 0; 969 tflg = 0; 970 Sflg = 0; 971 statreq = 0; 972 } 973 974 fixedwidth = 2; 975 if (pflg || Fflg) 976 fixedwidth++; 977 if (iflg) 978 fixedwidth += 11; 979 if (sflg) 980 fixedwidth += 5; 981 982 if (lflg) { 983 if (!gflg && !oflg) 984 gflg = oflg = 1; 985 else 986 if (gflg && oflg) 987 gflg = oflg = 0; 988 Cflg = mflg = 0; 989 } 990 991 if (!wflg && (Cflg || mflg)) { 992 char *clptr; 993 if ((clptr = getenv("COLUMNS")) != NULL) 994 num_cols = atoi(clptr); 995 #ifdef TERMINFO 996 else { 997 if (ioctl(1, TIOCGWINSZ, &win) != -1) 998 num_cols = (win.ws_col == 0 ? 80 : win.ws_col); 999 } 1000 #endif 1001 } 1002 1003 /* 1004 * When certain options (-f, or -U and -1, and not -l, etc.) are 1005 * specified, don't cache each dirent as it's read. This 'noflist' 1006 * option is set when there's no need to cache those dirents; instead, 1007 * print them out as they're read. 1008 */ 1009 if ((Uflg || fflg) && !Cflg && !lflg && !iflg && statreq == 0) 1010 noflist = 1; 1011 1012 if (num_cols < 20 || num_cols > 1000) 1013 /* assume it is an error */ 1014 num_cols = 80; 1015 1016 /* allocate space for flist and the associated */ 1017 /* data structures (lbufs) */ 1018 maxfils = quantn; 1019 if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) || 1020 ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) { 1021 perror("ls"); 1022 exit(2); 1023 } 1024 if ((amino = (argc-optind)) == 0) { 1025 /* 1026 * case when no names are given 1027 * in ls-command and current 1028 * directory is to be used 1029 */ 1030 argv[optind] = dotp; 1031 } 1032 1033 if (colorflg) 1034 ls_color_init(); 1035 1036 for (i = 0; i < (amino ? amino : 1); i++) { 1037 1038 /* 1039 * If we are recursing, we need to make sure we don't 1040 * get into an endless loop. To keep track of the inodes 1041 * (actually, just the directories) visited, we 1042 * maintain a directory ancestry list for a file 1043 * hierarchy. As we go deeper into the hierarchy, 1044 * a parent directory passes its directory list 1045 * info (device id, inode number, and a pointer to 1046 * its parent) to each of its children. As we 1047 * process a child that is a directory, we save 1048 * its own personal directory list info. We then 1049 * check to see if the child has already been 1050 * processed by comparing its device id and inode 1051 * number from its own personal directory list info 1052 * to that of each of its ancestors. If there is a 1053 * match, then we know we've detected a cycle. 1054 */ 1055 if (Rflg) { 1056 /* 1057 * This is the first parent in this lineage 1058 * (first in a directory hierarchy), so 1059 * this parent's parent doesn't exist. We 1060 * only initialize myinfo when we are 1061 * recursing, otherwise it's not used. 1062 */ 1063 if ((myinfo = (struct ditem *)malloc( 1064 sizeof (struct ditem))) == NULL) { 1065 perror("ls"); 1066 exit(2); 1067 } else { 1068 myinfo->dev = 0; 1069 myinfo->ino = 0; 1070 myinfo->parent = NULL; 1071 } 1072 } 1073 1074 if (Cflg || mflg) { 1075 width = strcol((unsigned char *)argv[optind]); 1076 if (width > filewidth) 1077 filewidth = width; 1078 } 1079 if ((ep = gstat((*argv[optind] ? argv[optind] : dotp), 1080 1, myinfo)) == NULL) { 1081 if (nomocore) 1082 exit(2); 1083 err = 2; 1084 optind++; 1085 continue; 1086 } 1087 ep->ln.namep = (*argv[optind] ? argv[optind] : dotp); 1088 ep->lflags |= ISARG; 1089 optind++; 1090 nargs++; /* count good arguments stored in flist */ 1091 if (acl_err) 1092 err = 2; 1093 } 1094 colwidth = fixedwidth + filewidth; 1095 if (!Uflg) 1096 qsort(flist, (unsigned)nargs, sizeof (struct lbuf *), 1097 (int (*)(const void *, const void *))compar); 1098 for (i = 0; i < nargs; i++) { 1099 if ((flist[i]->ltype == 'd' && dflg == 0) || fflg) 1100 break; 1101 } 1102 1103 pem(&flist[0], &flist[i], 0); 1104 for (; i < nargs; i++) { 1105 pdirectory(flist[i]->ln.namep, Rflg || 1106 (amino > 1), nargs, 0, flist[i]->ancinfo); 1107 if (nomocore) 1108 exit(2); 1109 /* -R: print subdirectories found */ 1110 while (dfirst || cdfirst) { 1111 /* Place direct subdirs on front in right order */ 1112 while (cdfirst) { 1113 /* reverse cdfirst onto front of dfirst */ 1114 dtemp = cdfirst; 1115 cdfirst = cdfirst -> dc_next; 1116 dtemp -> dc_next = dfirst; 1117 dfirst = dtemp; 1118 } 1119 /* take off first dir on dfirst & print it */ 1120 dtemp = dfirst; 1121 dfirst = dfirst->dc_next; 1122 pdirectory(dtemp->dc_name, 1, nargs, 1123 dtemp->cycle_detected, dtemp->myancinfo); 1124 if (nomocore) 1125 exit(2); 1126 free(dtemp->dc_name); 1127 free(dtemp); 1128 } 1129 } 1130 1131 return (err); 1132 } 1133 1134 /* 1135 * pdirectory: print the directory name, labelling it if title is 1136 * nonzero, using lp as the place to start reading in the dir. 1137 */ 1138 static void 1139 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo) 1140 { 1141 struct dchain *dp; 1142 struct lbuf *ap; 1143 char *pname; 1144 int j; 1145 1146 filewidth = 0; 1147 curdir = name; 1148 if (title) { 1149 if (!first) 1150 (void) putc('\n', stdout); 1151 pprintf(name, ":"); 1152 new_line(); 1153 } 1154 /* 1155 * If there was a cycle detected, then notify and don't report 1156 * further. 1157 */ 1158 if (cdetect) { 1159 if (lflg || sflg) { 1160 curcol += printf(gettext("total %d"), 0); 1161 new_line(); 1162 } 1163 (void) fprintf(stderr, gettext( 1164 "ls: cycle detected for %s\n"), name); 1165 return; 1166 } 1167 1168 nfiles = lp; 1169 rddir(name, myinfo); 1170 if (nomocore || noflist) 1171 return; 1172 if (fflg == 0 && Uflg == 0) 1173 qsort(&flist[lp], (unsigned)(nfiles - lp), 1174 sizeof (struct lbuf *), 1175 (int (*)(const void *, const void *))compar); 1176 if (Rflg) { 1177 for (j = nfiles - 1; j >= lp; j--) { 1178 ap = flist[j]; 1179 if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") && 1180 strcmp(ap->ln.lname, "..")) { 1181 dp = malloc(sizeof (struct dchain)); 1182 if (dp == NULL) { 1183 perror("ls"); 1184 exit(2); 1185 } 1186 pname = makename(curdir, ap->ln.lname); 1187 if ((dp->dc_name = strdup(pname)) == NULL) { 1188 perror("ls"); 1189 exit(2); 1190 } 1191 dp->cycle_detected = ap->cycle; 1192 dp->myancinfo = ap->ancinfo; 1193 dp->dc_next = dfirst; 1194 dfirst = dp; 1195 } 1196 } 1197 } 1198 if (lflg || sflg) { 1199 curcol += printf(gettext("total %llu"), tblocks); 1200 new_line(); 1201 } 1202 pem(&flist[lp], &flist[nfiles], lflg||sflg); 1203 } 1204 1205 /* 1206 * pem: print 'em. Print a list of files (e.g. a directory) bounded 1207 * by slp and lp. 1208 */ 1209 static void 1210 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag) 1211 { 1212 long row, nrows, i; 1213 int col, ncols = 1; 1214 struct lbuf **ep; 1215 1216 if (Cflg || mflg) { 1217 if (colwidth <= num_cols) { 1218 ncols = num_cols / colwidth; 1219 } 1220 } 1221 1222 if (ncols == 1 || mflg || xflg || !Cflg) { 1223 for (ep = slp; ep < lp; ep++) 1224 pentry(*ep); 1225 new_line(); 1226 return; 1227 } 1228 /* otherwise print -C columns */ 1229 if (tot_flag) { 1230 slp--; 1231 row = 1; 1232 } 1233 else 1234 row = 0; 1235 1236 nrows = (lp - slp - 1) / ncols + 1; 1237 for (i = 0; i < nrows; i++, row++) { 1238 for (col = 0; col < ncols; col++) { 1239 ep = slp + (nrows * col) + row; 1240 if (ep < lp) 1241 pentry(*ep); 1242 } 1243 new_line(); 1244 } 1245 } 1246 1247 /* 1248 * print one output entry; 1249 * if uid/gid is not found in the appropriate 1250 * file(passwd/group), then print uid/gid instead of 1251 * user/group name; 1252 */ 1253 static void 1254 pentry(struct lbuf *ap) 1255 { 1256 struct lbuf *p; 1257 numbuf_t hbuf; 1258 char *dmark = ""; /* Used if -p or -F option active */ 1259 char *cp; 1260 char *str; 1261 1262 if (noflist) { 1263 (void) printf("%s\n", ap->ln.lname); 1264 return; 1265 } 1266 1267 p = ap; 1268 column(); 1269 if (iflg) { 1270 if (mflg && !lflg) 1271 curcol += printf("%llu ", (long long)p->lnum); 1272 else 1273 curcol += printf("%10llu ", (long long)p->lnum); 1274 } 1275 if (sflg) { 1276 curcol += printf((mflg && !lflg) ? "%lld " : 1277 (p->lblocks < 10000) ? "%4lld " : "%lld ", 1278 (p->ltype != 'b' && p->ltype != 'c') ? 1279 p->lblocks : 0LL); 1280 } 1281 if (lflg) { 1282 (void) putchar(p->ltype); 1283 curcol++; 1284 pmode(p->lflags); 1285 1286 /* ACL: additional access mode flag */ 1287 (void) putchar(p->acl); 1288 curcol++; 1289 1290 curcol += printf("%3lu ", (ulong_t)p->lnl); 1291 if (oflg) { 1292 if (!nflg) { 1293 cp = getname(p->luid); 1294 curcol += printf("%-8s ", cp); 1295 } else 1296 curcol += printf("%-8lu ", (ulong_t)p->luid); 1297 } 1298 if (gflg) { 1299 if (!nflg) { 1300 cp = getgroup(p->lgid); 1301 curcol += printf("%-8s ", cp); 1302 } else 1303 curcol += printf("%-8lu ", (ulong_t)p->lgid); 1304 } 1305 if (p->ltype == 'b' || p->ltype == 'c') { 1306 curcol += printf("%3u, %2u", 1307 (uint_t)major((dev_t)p->lsize), 1308 (uint_t)minor((dev_t)p->lsize)); 1309 } else if (hflg && (p->lsize >= hscale)) { 1310 curcol += printf("%7s", 1311 number_to_scaled_string(hbuf, p->lsize, hscale)); 1312 } else { 1313 uint64_t bsize = p->lsize / block_size; 1314 1315 /* 1316 * Round up only when using blocks > 1 byte, otherwise 1317 * 'normal' sizes display 1 byte too large. 1318 */ 1319 if (p->lsize % block_size != 0) 1320 bsize++; 1321 1322 curcol += printf("%7" PRIu64, bsize); 1323 } 1324 format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec); 1325 /* format extended system attribute time */ 1326 if (tmflg && crtm) 1327 format_attrtime(p); 1328 1329 curcol += printf("%s", time_buf); 1330 1331 } 1332 /* 1333 * prevent both "->" and trailing marks 1334 * from appearing 1335 */ 1336 1337 if (pflg && p->ltype == 'd') 1338 dmark = "/"; 1339 1340 if (Fflg && !(lflg && p->flinkto)) { 1341 if (p->ltype == 'd') 1342 dmark = "/"; 1343 else if (p->ltype == 'D') 1344 dmark = ">"; 1345 else if (p->ltype == 'p') 1346 dmark = "|"; 1347 else if (p->ltype == 'l') 1348 dmark = "@"; 1349 else if (p->ltype == 's') 1350 dmark = "="; 1351 else if (!file_typeflg && 1352 (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH))) 1353 dmark = "*"; 1354 else 1355 dmark = ""; 1356 } 1357 1358 if (colorflg) 1359 ls_start_color(p->color); 1360 1361 if (p->lflags & ISARG) 1362 str = p->ln.namep; 1363 else 1364 str = p->ln.lname; 1365 1366 if (qflg || bflg) { 1367 csi_pprintf((unsigned char *)str); 1368 1369 if (lflg && p->flinkto) { 1370 if (colorflg) 1371 ls_end_color(); 1372 csi_pprintf((unsigned char *)" -> "); 1373 if (colorflg) 1374 ls_start_color(p->link_color); 1375 csi_pprintf((unsigned char *)p->flinkto); 1376 } else { 1377 csi_pprintf((unsigned char *)dmark); 1378 } 1379 } else { 1380 (void) printf("%s", str); 1381 curcol += strcol((unsigned char *)str); 1382 1383 if (lflg && p->flinkto) { 1384 if (colorflg) 1385 ls_end_color(); 1386 str = " -> "; 1387 (void) printf("%s", str); 1388 curcol += strcol((unsigned char *)str); 1389 if (colorflg) 1390 ls_start_color(p->link_color); 1391 (void) printf("%s", p->flinkto); 1392 curcol += strcol((unsigned char *)p->flinkto); 1393 } else { 1394 (void) printf("%s", dmark); 1395 curcol += strcol((unsigned char *)dmark); 1396 } 1397 } 1398 1399 if (colorflg) 1400 ls_end_color(); 1401 1402 /* Display extended system attributes */ 1403 if (saflg) { 1404 int i; 1405 1406 new_line(); 1407 (void) printf(" \t{"); 1408 if (p->exttr != NULL) { 1409 int k = 0; 1410 for (i = 0; i < sacnt; i++) { 1411 if (p->exttr[i].name != NULL) 1412 k++; 1413 } 1414 for (i = 0; i < sacnt; i++) { 1415 if (p->exttr[i].name != NULL) { 1416 (void) printf("%s", p->exttr[i].name); 1417 k--; 1418 if (vopt && (k != 0)) 1419 (void) printf(","); 1420 } 1421 } 1422 } 1423 (void) printf("}\n"); 1424 } 1425 /* Display file timestamps and extended system attribute timestamps */ 1426 if (tmflg && alltm) { 1427 new_line(); 1428 print_time(p); 1429 new_line(); 1430 } 1431 if (vflg) { 1432 new_line(); 1433 if (p->aclp) { 1434 acl_printacl(p->aclp, num_cols, Vflg); 1435 } 1436 } 1437 /* Free extended system attribute lists */ 1438 if (saflg || tmflg) 1439 free_sysattr(p); 1440 } 1441 1442 /* print various r,w,x permissions */ 1443 static void 1444 pmode(mode_t aflag) 1445 { 1446 /* these arrays are declared static to allow initializations */ 1447 static int m0[] = { 1, S_IRUSR, 'r', '-' }; 1448 static int m1[] = { 1, S_IWUSR, 'w', '-' }; 1449 static int m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR, 1450 'x', S_ISUID, 'S', '-' }; 1451 static int m3[] = { 1, S_IRGRP, 'r', '-' }; 1452 static int m4[] = { 1, S_IWGRP, 'w', '-' }; 1453 static int m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP, 1454 'x', S_ISGID|LS_NOTREG, 'S', 1455 #ifdef XPG4 1456 S_ISGID, 'L', '-'}; 1457 #else 1458 S_ISGID, 'l', '-'}; 1459 #endif 1460 static int m6[] = { 1, S_IROTH, 'r', '-' }; 1461 static int m7[] = { 1, S_IWOTH, 'w', '-' }; 1462 static int m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH, 1463 'x', S_ISVTX, 'T', '-'}; 1464 1465 static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8}; 1466 1467 int **mp; 1468 1469 flags = aflag; 1470 for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++) 1471 selection(*mp); 1472 } 1473 1474 static void 1475 selection(int *pairp) 1476 { 1477 int n; 1478 1479 n = *pairp++; 1480 while (n-->0) { 1481 if ((flags & *pairp) == *pairp) { 1482 pairp++; 1483 break; 1484 } else { 1485 pairp += 2; 1486 } 1487 } 1488 (void) putchar(*pairp); 1489 curcol++; 1490 } 1491 1492 /* 1493 * column: get to the beginning of the next column. 1494 */ 1495 static void 1496 column(void) 1497 { 1498 if (curcol == 0) 1499 return; 1500 if (mflg) { 1501 (void) putc(',', stdout); 1502 curcol++; 1503 if (curcol + colwidth + 2 > num_cols) { 1504 (void) putc('\n', stdout); 1505 curcol = 0; 1506 return; 1507 } 1508 (void) putc(' ', stdout); 1509 curcol++; 1510 return; 1511 } 1512 if (Cflg == 0) { 1513 (void) putc('\n', stdout); 1514 curcol = 0; 1515 return; 1516 } 1517 if ((curcol / colwidth + 2) * colwidth > num_cols) { 1518 (void) putc('\n', stdout); 1519 curcol = 0; 1520 return; 1521 } 1522 do { 1523 (void) putc(' ', stdout); 1524 curcol++; 1525 } while (curcol % colwidth); 1526 } 1527 1528 static void 1529 new_line(void) 1530 { 1531 if (curcol) { 1532 first = 0; 1533 (void) putc('\n', stdout); 1534 curcol = 0; 1535 } 1536 } 1537 1538 /* 1539 * read each filename in directory dir and store its 1540 * status in flist[nfiles] 1541 * use makename() to form pathname dir/filename; 1542 */ 1543 static void 1544 rddir(char *dir, struct ditem *myinfo) 1545 { 1546 struct dirent *dentry; 1547 DIR *dirf; 1548 int j; 1549 struct lbuf *ep; 1550 int width; 1551 1552 if ((dirf = opendir(dir)) == NULL) { 1553 (void) fflush(stdout); 1554 perror(dir); 1555 err = 2; 1556 return; 1557 } else { 1558 tblocks = 0; 1559 for (;;) { 1560 errno = 0; 1561 if ((dentry = readdir(dirf)) == NULL) 1562 break; 1563 if (aflg == 0 && dentry->d_name[0] == '.' && 1564 (Aflg == 0 || 1565 dentry->d_name[1] == '\0' || 1566 (dentry->d_name[1] == '.' && 1567 dentry->d_name[2] == '\0'))) 1568 /* 1569 * check for directory items '.', '..', 1570 * and items without valid inode-number; 1571 */ 1572 continue; 1573 1574 /* skip entries ending in ~ if -B was given */ 1575 if (Bflg && 1576 dentry->d_name[strlen(dentry->d_name) - 1] == '~') 1577 continue; 1578 if (Cflg || mflg) { 1579 width = strcol((unsigned char *)dentry->d_name); 1580 if (width > filewidth) 1581 filewidth = width; 1582 } 1583 ep = gstat(makename(dir, dentry->d_name), 0, myinfo); 1584 if (ep == NULL) { 1585 if (nomocore) 1586 exit(2); 1587 continue; 1588 } else { 1589 ep->lnum = dentry->d_ino; 1590 for (j = 0; dentry->d_name[j] != '\0'; j++) 1591 ep->ln.lname[j] = dentry->d_name[j]; 1592 ep->ln.lname[j] = '\0'; 1593 1594 /* 1595 * Since this entry doesn't need to be sorted 1596 * or further processed, print it right away. 1597 */ 1598 if (noflist) { 1599 pem(&ep, &ep + 1, 0); 1600 nfiles--; 1601 } 1602 } 1603 } 1604 if (errno) { 1605 int sav_errno = errno; 1606 1607 (void) fprintf(stderr, 1608 gettext("ls: error reading directory %s: %s\n"), 1609 dir, strerror(sav_errno)); 1610 } 1611 (void) closedir(dirf); 1612 colwidth = fixedwidth + filewidth; 1613 } 1614 } 1615 1616 /* 1617 * Attaching a link to an inode's ancestors. Search 1618 * through the ancestors to check for cycles (an inode which 1619 * we have already tracked in this inodes ancestry). If a cycle 1620 * is detected, set the exit code and record the fact so that 1621 * it is reported at the right time when printing the directory. 1622 * In addition, set the exit code. Note: If the -a flag was 1623 * specified, we don't want to check for cycles for directories 1624 * ending in '/.' or '/..' unless they were specified on the 1625 * command line. 1626 */ 1627 static void 1628 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep, 1629 int argfl, struct ditem *myparent) 1630 { 1631 size_t file_len; 1632 struct ditem *myinfo; 1633 struct ditem *tptr; 1634 1635 file_len = strlen(file); 1636 if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) && 1637 NOTPARENTDIR(file, file_len))) { 1638 /* 1639 * Add this inode's ancestry 1640 * info and insert it into the 1641 * ancestry list by pointing 1642 * back to its parent. We save 1643 * it (in rep) with the other info 1644 * we're gathering for this inode. 1645 */ 1646 if ((myinfo = malloc( 1647 sizeof (struct ditem))) == NULL) { 1648 perror("ls"); 1649 exit(2); 1650 } 1651 myinfo->dev = pstatb->st_dev; 1652 myinfo->ino = pstatb->st_ino; 1653 myinfo->parent = myparent; 1654 rep->ancinfo = myinfo; 1655 1656 /* 1657 * If this node has the same device id and 1658 * inode number of one of its ancestors, 1659 * then we've detected a cycle. 1660 */ 1661 if (myparent != NULL) { 1662 for (tptr = myparent; tptr->parent != NULL; 1663 tptr = tptr->parent) { 1664 if ((tptr->dev == pstatb->st_dev) && 1665 (tptr->ino == pstatb->st_ino)) { 1666 /* 1667 * Cycle detected for this 1668 * directory. Record the fact 1669 * it is a cycle so we don't 1670 * try to process this 1671 * directory as we are 1672 * walking through the 1673 * list of directories. 1674 */ 1675 rep->cycle = 1; 1676 err = 2; 1677 break; 1678 } 1679 } 1680 } 1681 } 1682 } 1683 1684 /* 1685 * Do re-calculate the mode for group for ACE_T type of acls. 1686 * This is because, if the server's FS happens to be UFS, supporting 1687 * POSIX ACL's, then it does a special calculation of group mode 1688 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.) 1689 * 1690 * This algorithm is from the NFSv4 ACL Draft. Here a part of that 1691 * algorithm is used for the group mode calculation only. 1692 * What is modified here from the algorithm is that only the 1693 * entries with flags ACE_GROUP are considered. For each entry 1694 * with ACE_GROUP flag, the first occurance of a specific access 1695 * is checked if it is allowed. 1696 * We are not interested in perms for user and other, as they 1697 * were taken from st_mode value. 1698 * We are not interested in a_who field of ACE, as we need just 1699 * unix mode bits for the group. 1700 */ 1701 1702 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP) 1703 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE) 1704 1705 int 1706 grp_mask_to_mode(struct lbuf *p) 1707 { 1708 int mode = 0, seen = 0; 1709 int acecnt; 1710 int flags; 1711 ace_t *ap; 1712 acl_t *acep = p->aclp; 1713 1714 acecnt = acl_cnt(acep); 1715 for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) { 1716 1717 if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE && 1718 ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE) 1719 continue; 1720 1721 if (ap->a_flags & ACE_INHERIT_ONLY_ACE) 1722 continue; 1723 1724 /* 1725 * if it is first group@ or first everyone@ 1726 * for each of read, write and execute, then 1727 * that will be the group mode bit. 1728 */ 1729 flags = ap->a_flags & ACE_TYPE_FLAGS; 1730 if (flags == OWNED_GROUP || (flags == ACE_IDENTIFIER_GROUP && 1731 ap->a_who == p->lgid) || flags == ACE_EVERYONE) { 1732 if (ap->a_access_mask & ACE_READ_DATA) { 1733 if (!(seen & S_IRGRP)) { 1734 seen |= S_IRGRP; 1735 if (IS_TYPE_ALLOWED(ap->a_type)) 1736 mode |= S_IRGRP; 1737 } 1738 } 1739 if (ap->a_access_mask & ACE_WRITE_DATA) { 1740 if (!(seen & S_IWGRP)) { 1741 seen |= S_IWGRP; 1742 if (IS_TYPE_ALLOWED(ap->a_type)) 1743 mode |= S_IWGRP; 1744 } 1745 } 1746 if (ap->a_access_mask & ACE_EXECUTE) { 1747 if (!(seen & S_IXGRP)) { 1748 seen |= S_IXGRP; 1749 if (IS_TYPE_ALLOWED(ap->a_type)) 1750 mode |= S_IXGRP; 1751 } 1752 } 1753 } 1754 } 1755 return (mode); 1756 } 1757 1758 /* 1759 * get status of file and recomputes tblocks; 1760 * argfl = 1 if file is a name in ls-command and = 0 1761 * for filename in a directory whose name is an 1762 * argument in the command; 1763 * stores a pointer in flist[nfiles] and 1764 * returns that pointer; 1765 * returns NULL if failed; 1766 */ 1767 static struct lbuf * 1768 gstat(char *file, int argfl, struct ditem *myparent) 1769 { 1770 struct stat statb, statb1; 1771 struct lbuf *rep; 1772 char buf[BUFSIZ]; 1773 ssize_t cc; 1774 int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat; 1775 int aclcnt; 1776 int error; 1777 aclent_t *tp; 1778 o_mode_t groupperm, mask; 1779 int grouppermfound, maskfound; 1780 1781 if (nomocore) 1782 return (NULL); 1783 1784 if (nfiles >= maxfils) { 1785 /* 1786 * all flist/lbuf pair assigned files, time to get some 1787 * more space 1788 */ 1789 maxfils += quantn; 1790 if (((flist = realloc(flist, 1791 maxfils * sizeof (struct lbuf *))) == NULL) || 1792 ((nxtlbf = malloc(quantn * 1793 sizeof (struct lbuf))) == NULL)) { 1794 perror("ls"); 1795 nomocore = 1; 1796 return (NULL); 1797 } 1798 } 1799 1800 /* 1801 * nfiles is reset to nargs for each directory 1802 * that is given as an argument maxn is checked 1803 * to prevent the assignment of an lbuf to a flist entry 1804 * that already has one assigned. 1805 */ 1806 if (nfiles >= maxn) { 1807 rep = nxtlbf++; 1808 flist[nfiles++] = rep; 1809 maxn = nfiles; 1810 } else { 1811 rep = flist[nfiles++]; 1812 } 1813 1814 /* 1815 * When noflist is set, none of the extra information about the dirent 1816 * will be printed, so omit initialization of this lbuf as well as the 1817 * stat(2) call. 1818 */ 1819 if (!argfl && noflist) 1820 return (rep); 1821 1822 /* Initialize */ 1823 1824 rep->lflags = (mode_t)0; 1825 rep->flinkto = NULL; 1826 rep->cycle = 0; 1827 rep->lat.tv_sec = time(NULL); 1828 rep->lat.tv_nsec = 0; 1829 rep->lct.tv_sec = time(NULL); 1830 rep->lct.tv_nsec = 0; 1831 rep->lmt.tv_sec = time(NULL); 1832 rep->lmt.tv_nsec = 0; 1833 rep->aclp = NULL; 1834 rep->exttr = NULL; 1835 rep->extm = NULL; 1836 rep->color = NULL; 1837 rep->link_color = NULL; 1838 1839 if (argfl || statreq) { 1840 int doacl; 1841 1842 if (lflg) 1843 doacl = 1; 1844 else 1845 doacl = 0; 1846 1847 if ((*statf)(file, &statb) < 0) { 1848 if (argfl || errno != ENOENT || 1849 (Lflg && lstat(file, &statb) == 0)) { 1850 /* 1851 * Avoid race between readdir and lstat. 1852 * Print error message in case of dangling link. 1853 */ 1854 perror(file); 1855 err = 2; 1856 } 1857 nfiles--; 1858 return (NULL); 1859 } 1860 1861 /* 1862 * If -H was specified, and the file linked to was 1863 * not a directory, then we need to get the info 1864 * for the symlink itself. 1865 */ 1866 if ((Hflg) && (argfl) && 1867 ((statb.st_mode & S_IFMT) != S_IFDIR)) { 1868 if (lstat(file, &statb) < 0) { 1869 perror(file); 1870 err = 2; 1871 } 1872 } 1873 1874 rep->lnum = statb.st_ino; 1875 rep->lsize = statb.st_size; 1876 rep->lblocks = statb.st_blocks; 1877 if (colorflg) 1878 rep->color = ls_color_find(file, statb.st_mode); 1879 1880 switch (statb.st_mode & S_IFMT) { 1881 case S_IFDIR: 1882 rep->ltype = 'd'; 1883 if (Rflg) { 1884 record_ancestry(file, &statb, rep, 1885 argfl, myparent); 1886 } 1887 break; 1888 case S_IFBLK: 1889 rep->ltype = 'b'; 1890 rep->lsize = (off_t)statb.st_rdev; 1891 break; 1892 case S_IFCHR: 1893 rep->ltype = 'c'; 1894 rep->lsize = (off_t)statb.st_rdev; 1895 break; 1896 case S_IFIFO: 1897 rep->ltype = 'p'; 1898 break; 1899 case S_IFSOCK: 1900 rep->ltype = 's'; 1901 rep->lsize = 0; 1902 break; 1903 case S_IFLNK: 1904 /* symbolic links may not have ACLs, so elide acl() */ 1905 if ((Lflg == 0) || (Hflg == 0) || 1906 ((Hflg) && (!argfl))) { 1907 doacl = 0; 1908 } 1909 rep->ltype = 'l'; 1910 if (lflg || colorflg) { 1911 cc = readlink(file, buf, BUFSIZ); 1912 if (cc < 0) 1913 break; 1914 1915 /* 1916 * follow the symbolic link 1917 * to generate the appropriate 1918 * Fflg marker for the object 1919 * eg, /bin -> /sym/bin/ 1920 */ 1921 error = 0; 1922 if (Fflg || pflg || colorflg) 1923 error = stat(file, &statb1); 1924 1925 if (colorflg) { 1926 if (error >= 0) 1927 rep->link_color = 1928 ls_color_find(file, 1929 statb1.st_mode); 1930 else 1931 rep->link_color = 1932 lsc_orphan; 1933 } 1934 1935 if ((Fflg || pflg) && error >= 0) { 1936 switch (statb1.st_mode & S_IFMT) { 1937 case S_IFDIR: 1938 buf[cc++] = '/'; 1939 break; 1940 case S_IFSOCK: 1941 buf[cc++] = '='; 1942 break; 1943 case S_IFDOOR: 1944 buf[cc++] = '>'; 1945 break; 1946 case S_IFIFO: 1947 buf[cc++] = '|'; 1948 break; 1949 default: 1950 if ((statb1.st_mode & ~S_IFMT) & 1951 (S_IXUSR|S_IXGRP| S_IXOTH)) 1952 buf[cc++] = '*'; 1953 break; 1954 } 1955 } 1956 buf[cc] = '\0'; 1957 rep->flinkto = strdup(buf); 1958 if (rep->flinkto == NULL) { 1959 perror("ls"); 1960 nomocore = 1; 1961 return (NULL); 1962 } 1963 break; 1964 } 1965 1966 /* 1967 * ls /sym behaves differently from ls /sym/ 1968 * when /sym is a symbolic link. This is fixed 1969 * when explicit arguments are specified. 1970 */ 1971 1972 #ifdef XPG6 1973 /* Do not follow a symlink when -F is specified */ 1974 if ((!argfl) || (argfl && Fflg) || 1975 (stat(file, &statb1) < 0)) 1976 #else 1977 /* Follow a symlink when -F is specified */ 1978 if (!argfl || stat(file, &statb1) < 0) 1979 #endif /* XPG6 */ 1980 break; 1981 if ((statb1.st_mode & S_IFMT) == S_IFDIR) { 1982 statb = statb1; 1983 rep->ltype = 'd'; 1984 rep->lsize = statb1.st_size; 1985 if (Rflg) { 1986 record_ancestry(file, &statb, rep, 1987 argfl, myparent); 1988 } 1989 } 1990 break; 1991 case S_IFDOOR: 1992 rep->ltype = 'D'; 1993 break; 1994 case S_IFREG: 1995 rep->ltype = '-'; 1996 break; 1997 case S_IFPORT: 1998 rep->ltype = 'P'; 1999 break; 2000 default: 2001 rep->ltype = '?'; 2002 break; 2003 } 2004 rep->lflags = statb.st_mode & ~S_IFMT; 2005 2006 if (!S_ISREG(statb.st_mode)) 2007 rep->lflags |= LS_NOTREG; 2008 2009 rep->luid = statb.st_uid; 2010 rep->lgid = statb.st_gid; 2011 rep->lnl = statb.st_nlink; 2012 if (uflg || (tmflg && atm)) 2013 rep->lmtime = statb.st_atim; 2014 else if (cflg || (tmflg && ctm)) 2015 rep->lmtime = statb.st_ctim; 2016 else 2017 rep->lmtime = statb.st_mtim; 2018 rep->lat = statb.st_atim; 2019 rep->lct = statb.st_ctim; 2020 rep->lmt = statb.st_mtim; 2021 2022 /* ACL: check acl entries count */ 2023 if (doacl) { 2024 2025 error = acl_get(file, 0, &rep->aclp); 2026 if (error) { 2027 (void) fprintf(stderr, 2028 gettext("ls: can't read ACL on %s: %s\n"), 2029 file, acl_strerror(error)); 2030 rep->acl = ' '; 2031 acl_err++; 2032 return (rep); 2033 } 2034 2035 rep->acl = ' '; 2036 2037 if (rep->aclp && 2038 ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) { 2039 rep->acl = '+'; 2040 /* 2041 * Special handling for ufs aka aclent_t ACL's 2042 */ 2043 if (acl_type(rep->aclp) == ACLENT_T) { 2044 /* 2045 * For files with non-trivial acls, the 2046 * effective group permissions are the 2047 * intersection of the GROUP_OBJ value 2048 * and the CLASS_OBJ (acl mask) value. 2049 * Determine both the GROUP_OBJ and 2050 * CLASS_OBJ for this file and insert 2051 * the logical AND of those two values 2052 * in the group permissions field 2053 * of the lflags value for this file. 2054 */ 2055 2056 /* 2057 * Until found in acl list, assume 2058 * maximum permissions for both group 2059 * a nd mask. (Just in case the acl 2060 * lacks either value for some reason.) 2061 */ 2062 groupperm = 07; 2063 mask = 07; 2064 grouppermfound = 0; 2065 maskfound = 0; 2066 aclcnt = acl_cnt(rep->aclp); 2067 for (tp = 2068 (aclent_t *)acl_data(rep->aclp); 2069 aclcnt--; tp++) { 2070 if (tp->a_type == GROUP_OBJ) { 2071 groupperm = tp->a_perm; 2072 grouppermfound = 1; 2073 continue; 2074 } 2075 if (tp->a_type == CLASS_OBJ) { 2076 mask = tp->a_perm; 2077 maskfound = 1; 2078 } 2079 if (grouppermfound && maskfound) 2080 break; 2081 } 2082 2083 2084 /* reset all the group bits */ 2085 rep->lflags &= ~S_IRWXG; 2086 2087 /* 2088 * Now set them to the logical AND of 2089 * the GROUP_OBJ permissions and the 2090 * acl mask. 2091 */ 2092 2093 rep->lflags |= (groupperm & mask) << 3; 2094 2095 } else if (acl_type(rep->aclp) == ACE_T) { 2096 int mode; 2097 mode = grp_mask_to_mode(rep); 2098 rep->lflags &= ~S_IRWXG; 2099 rep->lflags |= mode; 2100 } 2101 } 2102 2103 if (!vflg && !Vflg && rep->aclp) { 2104 acl_free(rep->aclp); 2105 rep->aclp = NULL; 2106 } 2107 2108 if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1) 2109 rep->acl = '@'; 2110 2111 } else 2112 rep->acl = ' '; 2113 2114 /* mask ISARG and other file-type bits */ 2115 2116 if (rep->ltype != 'b' && rep->ltype != 'c') 2117 tblocks += rep->lblocks; 2118 2119 /* Get extended system attributes */ 2120 2121 if ((saflg || (tmflg && crtm) || (tmflg && alltm)) && 2122 (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) { 2123 int i; 2124 2125 sacnt = attr_count(); 2126 /* 2127 * Allocate 'sacnt' size array to hold extended 2128 * system attribute name (verbose) or respective 2129 * symbol represenation (compact). 2130 */ 2131 rep->exttr = xmalloc(sacnt * sizeof (struct attrb), 2132 rep); 2133 2134 /* initialize boolean attribute list */ 2135 for (i = 0; i < sacnt; i++) 2136 rep->exttr[i].name = NULL; 2137 if (get_sysxattr(file, rep) != 0) { 2138 (void) fprintf(stderr, 2139 gettext("ls:Failed to retrieve " 2140 "extended system attribute from " 2141 "%s\n"), file); 2142 rep->exttr[0].name = xmalloc(2, rep); 2143 (void) strlcpy(rep->exttr[0].name, "?", 2); 2144 } 2145 } 2146 } 2147 return (rep); 2148 } 2149 2150 /* 2151 * returns pathname of the form dir/file; 2152 * dir and file are null-terminated strings. 2153 */ 2154 static char * 2155 makename(char *dir, char *file) 2156 { 2157 /* 2158 * PATH_MAX is the maximum length of a path name. 2159 * MAXNAMLEN is the maximum length of any path name component. 2160 * Allocate space for both, plus the '/' in the middle 2161 * and the null character at the end. 2162 * dfile is static as this is returned by makename(). 2163 */ 2164 static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1]; 2165 char *dp, *fp; 2166 2167 dp = dfile; 2168 fp = dir; 2169 while (*fp) 2170 *dp++ = *fp++; 2171 if (dp > dfile && *(dp - 1) != '/') 2172 *dp++ = '/'; 2173 fp = file; 2174 while (*fp) 2175 *dp++ = *fp++; 2176 *dp = '\0'; 2177 return (dfile); 2178 } 2179 2180 2181 #include <pwd.h> 2182 #include <grp.h> 2183 #include <utmpx.h> 2184 2185 struct utmpx utmp; 2186 2187 #define NMAX (sizeof (utmp.ut_name)) 2188 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 2189 2190 2191 struct cachenode { /* this struct must be zeroed before using */ 2192 struct cachenode *lesschild; /* subtree whose entries < val */ 2193 struct cachenode *grtrchild; /* subtree whose entries > val */ 2194 long val; /* the uid or gid of this entry */ 2195 int initted; /* name has been filled in */ 2196 char name[NMAX+1]; /* the string that val maps to */ 2197 }; 2198 static struct cachenode *names, *groups; 2199 2200 static struct cachenode * 2201 findincache(struct cachenode **head, long val) 2202 { 2203 struct cachenode **parent = head; 2204 struct cachenode *c = *parent; 2205 2206 while (c != NULL) { 2207 if (val == c->val) { 2208 /* found it */ 2209 return (c); 2210 } else if (val < c->val) { 2211 parent = &c->lesschild; 2212 c = c->lesschild; 2213 } else { 2214 parent = &c->grtrchild; 2215 c = c->grtrchild; 2216 } 2217 } 2218 2219 /* not in the cache, make a new entry for it */ 2220 c = calloc(1, sizeof (struct cachenode)); 2221 if (c == NULL) { 2222 perror("ls"); 2223 exit(2); 2224 } 2225 *parent = c; 2226 c->val = val; 2227 return (c); 2228 } 2229 2230 /* 2231 * get name from cache, or passwd file for a given uid; 2232 * lastuid is set to uid. 2233 */ 2234 static char * 2235 getname(uid_t uid) 2236 { 2237 struct passwd *pwent; 2238 struct cachenode *c; 2239 2240 if ((uid == lastuid) && lastuname) 2241 return (lastuname); 2242 2243 c = findincache(&names, uid); 2244 if (c->initted == 0) { 2245 if ((pwent = getpwuid(uid)) != NULL) { 2246 SCPYN(&c->name[0], pwent->pw_name); 2247 } else { 2248 (void) sprintf(&c->name[0], "%-8u", (int)uid); 2249 } 2250 c->initted = 1; 2251 } 2252 lastuid = uid; 2253 lastuname = &c->name[0]; 2254 return (lastuname); 2255 } 2256 2257 /* 2258 * get name from cache, or group file for a given gid; 2259 * lastgid is set to gid. 2260 */ 2261 static char * 2262 getgroup(gid_t gid) 2263 { 2264 struct group *grent; 2265 struct cachenode *c; 2266 2267 if ((gid == lastgid) && lastgname) 2268 return (lastgname); 2269 2270 c = findincache(&groups, gid); 2271 if (c->initted == 0) { 2272 if ((grent = getgrgid(gid)) != NULL) { 2273 SCPYN(&c->name[0], grent->gr_name); 2274 } else { 2275 (void) sprintf(&c->name[0], "%-8u", (int)gid); 2276 } 2277 c->initted = 1; 2278 } 2279 lastgid = gid; 2280 lastgname = &c->name[0]; 2281 return (lastgname); 2282 } 2283 2284 /* return >0 if item pointed by pp2 should appear first */ 2285 static int 2286 compar(struct lbuf **pp1, struct lbuf **pp2) 2287 { 2288 struct lbuf *p1, *p2; 2289 2290 p1 = *pp1; 2291 p2 = *pp2; 2292 if (dflg == 0) { 2293 /* 2294 * compare two names in ls-command one of which is file 2295 * and the other is a directory; 2296 * this portion is not used for comparing files within 2297 * a directory name of ls-command; 2298 */ 2299 if (p1->lflags&ISARG && p1->ltype == 'd') { 2300 if (!(p2->lflags&ISARG && p2->ltype == 'd')) 2301 return (1); 2302 } else { 2303 if (p2->lflags&ISARG && p2->ltype == 'd') 2304 return (-1); 2305 } 2306 } 2307 if (tflg) { 2308 if (p2->lmtime.tv_sec > p1->lmtime.tv_sec) 2309 return (rflg); 2310 else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec) 2311 return (-rflg); 2312 /* times are equal to the sec, check nsec */ 2313 if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec) 2314 return (rflg); 2315 else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec) 2316 return (-rflg); 2317 /* if times are equal, fall through and sort by name */ 2318 } else if (Sflg) { 2319 /* 2320 * The size stored in lsize can be either the 2321 * size or the major minor number (in the case of 2322 * block and character special devices). If it's 2323 * a major minor number, then the size is considered 2324 * to be zero and we want to fall through and sort 2325 * by name. In addition, if the size of p2 is equal 2326 * to the size of p1 we want to fall through and 2327 * sort by name. 2328 */ 2329 off_t p1size = (p1->ltype == 'b') || 2330 (p1->ltype == 'c') ? 0 : p1->lsize; 2331 off_t p2size = (p2->ltype == 'b') || 2332 (p2->ltype == 'c') ? 0 : p2->lsize; 2333 if (p2size > p1size) { 2334 return (rflg); 2335 } else if (p2size < p1size) { 2336 return (-rflg); 2337 } 2338 /* Sizes are equal, fall through and sort by name. */ 2339 } 2340 return (rflg * strcoll( 2341 p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname, 2342 p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname)); 2343 } 2344 2345 static void 2346 pprintf(char *s1, char *s2) 2347 { 2348 csi_pprintf((unsigned char *)s1); 2349 csi_pprintf((unsigned char *)s2); 2350 } 2351 2352 static void 2353 csi_pprintf(unsigned char *s) 2354 { 2355 unsigned char *cp; 2356 char c; 2357 int i; 2358 int c_len; 2359 int p_col; 2360 wchar_t pcode; 2361 2362 if (!qflg && !bflg) { 2363 for (cp = s; *cp != '\0'; cp++) { 2364 (void) putchar(*cp); 2365 curcol++; 2366 } 2367 return; 2368 } 2369 2370 for (cp = s; *cp; ) { 2371 if (isascii(c = *cp)) { 2372 if (!isprint(c)) { 2373 if (qflg) { 2374 c = '?'; 2375 } else { 2376 curcol += 3; 2377 (void) putc('\\', stdout); 2378 c = '0' + ((*cp >> 6) & 07); 2379 (void) putc(c, stdout); 2380 c = '0' + ((*cp >> 3) & 07); 2381 (void) putc(c, stdout); 2382 c = '0' + (*cp & 07); 2383 } 2384 } 2385 curcol++; 2386 cp++; 2387 (void) putc(c, stdout); 2388 continue; 2389 } 2390 2391 if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) { 2392 c_len = 1; 2393 goto not_print; 2394 } 2395 2396 if ((p_col = wcwidth(pcode)) > 0) { 2397 (void) putwchar(pcode); 2398 cp += c_len; 2399 curcol += p_col; 2400 continue; 2401 } 2402 2403 not_print: 2404 for (i = 0; i < c_len; i++) { 2405 if (qflg) { 2406 c = '?'; 2407 } else { 2408 curcol += 3; 2409 (void) putc('\\', stdout); 2410 c = '0' + ((*cp >> 6) & 07); 2411 (void) putc(c, stdout); 2412 c = '0' + ((*cp >> 3) & 07); 2413 (void) putc(c, stdout); 2414 c = '0' + (*cp & 07); 2415 } 2416 curcol++; 2417 (void) putc(c, stdout); 2418 cp++; 2419 } 2420 } 2421 } 2422 2423 static int 2424 strcol(unsigned char *s1) 2425 { 2426 int w; 2427 int w_col; 2428 int len; 2429 wchar_t wc; 2430 2431 w = 0; 2432 while (*s1) { 2433 if (isascii(*s1)) { 2434 w++; 2435 s1++; 2436 continue; 2437 } 2438 2439 if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) { 2440 w++; 2441 s1++; 2442 continue; 2443 } 2444 2445 if ((w_col = wcwidth(wc)) < 0) 2446 w_col = len; 2447 s1 += len; 2448 w += w_col; 2449 } 2450 return (w); 2451 } 2452 2453 /* 2454 * Convert an unsigned long long to a string representation and place the 2455 * result in the caller-supplied buffer. 2456 * 2457 * The number provided is a size in bytes. The number is first 2458 * converted to an integral multiple of 'scale' bytes. This new 2459 * number is then scaled down until it is small enough to be in a good 2460 * human readable format, i.e. in the range 0 thru scale-1. If the 2461 * number used to derive the final number is not a multiple of scale, and 2462 * the final number has only a single significant digit, we compute 2463 * tenths of units to provide a second significant digit. 2464 * 2465 * The value "(unsigned long long)-1" is a special case and is always 2466 * converted to "-1". 2467 * 2468 * A pointer to the caller-supplied buffer is returned. 2469 */ 2470 static char * 2471 number_to_scaled_string( 2472 numbuf_t buf, /* put the result here */ 2473 unsigned long long number, /* convert this number */ 2474 long scale) 2475 { 2476 unsigned long long save; 2477 /* Measurement: kilo, mega, giga, tera, peta, exa */ 2478 char *uom = "KMGTPE"; 2479 2480 if ((long long)number == (long long)-1) { 2481 (void) strlcpy(buf, "-1", sizeof (numbuf_t)); 2482 return (buf); 2483 } 2484 2485 save = number; 2486 number = number / scale; 2487 2488 /* 2489 * Now we have number as a count of scale units. 2490 * If no further scaling is necessary, we round up as appropriate. 2491 * 2492 * The largest value number could have had entering the routine is 2493 * 16 Exabytes, so running off the end of the uom array should 2494 * never happen. We check for that, though, as a guard against 2495 * a breakdown elsewhere in the algorithm. 2496 */ 2497 if (number < (unsigned long long)scale) { 2498 if ((save % scale) >= (unsigned long long)(scale / 2)) { 2499 if (++number == (unsigned long long)scale) { 2500 uom++; 2501 number = 1; 2502 } 2503 } 2504 } else { 2505 while ((number >= (unsigned long long)scale) && (*uom != 'E')) { 2506 uom++; /* next unit of measurement */ 2507 save = number; 2508 /* 2509 * If we're over half way to the next unit of 2510 * 'scale' bytes (which means we should round 2511 * up), then adding half of 'scale' prior to 2512 * the division will push us into that next 2513 * unit of scale when we perform the division 2514 */ 2515 number = (number + (scale / 2)) / scale; 2516 } 2517 } 2518 2519 /* check if we should output a decimal place after the point */ 2520 if ((save / scale) < 10) { 2521 /* snprintf() will round for us */ 2522 float fnum = (float)save / scale; 2523 (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c", 2524 fnum, *uom); 2525 } else { 2526 (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c", 2527 number, *uom); 2528 } 2529 return (buf); 2530 } 2531 2532 /* Get extended system attributes and set the display */ 2533 2534 int 2535 get_sysxattr(char *fname, struct lbuf *rep) 2536 { 2537 boolean_t value; 2538 data_type_t type; 2539 int error; 2540 char *name; 2541 int i; 2542 2543 if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname, 2544 &response)) != 0) { 2545 perror("ls:getattrat"); 2546 return (error); 2547 } 2548 2549 /* 2550 * Allocate 'sacnt' size array to hold extended timestamp 2551 * system attributes and initialize the array. 2552 */ 2553 rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep); 2554 for (i = 0; i < sacnt; i++) { 2555 rep->extm[i].stm = 0; 2556 rep->extm[i].nstm = 0; 2557 rep->extm[i].name = NULL; 2558 } 2559 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) { 2560 name = nvpair_name(pair); 2561 type = nvpair_type(pair); 2562 if (type == DATA_TYPE_BOOLEAN_VALUE) { 2563 error = nvpair_value_boolean_value(pair, &value); 2564 if (error) { 2565 (void) fprintf(stderr, 2566 gettext("nvpair_value_boolean_value " 2567 "failed: error = %d\n"), error); 2568 continue; 2569 } 2570 if (name != NULL) 2571 set_sysattrb_display(name, value, rep); 2572 continue; 2573 } else if (type == DATA_TYPE_UINT64_ARRAY) { 2574 if (name != NULL) 2575 set_sysattrtm_display(name, rep); 2576 continue; 2577 } 2578 } 2579 nvlist_free(response); 2580 return (0); 2581 } 2582 2583 /* Set extended system attribute boolean display */ 2584 2585 void 2586 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep) 2587 { 2588 f_attr_t fattr; 2589 const char *opt; 2590 size_t len; 2591 2592 fattr = name_to_attr(name); 2593 if (fattr != F_ATTR_INVAL && fattr < sacnt) { 2594 if (vopt) { 2595 len = strlen(name); 2596 if (val) { 2597 rep->exttr[fattr].name = xmalloc(len + 1, rep); 2598 (void) strlcpy(rep->exttr[fattr].name, name, 2599 len + 1); 2600 } else { 2601 rep->exttr[fattr].name = xmalloc(len + 3, rep); 2602 (void) snprintf(rep->exttr[fattr].name, len + 3, 2603 "no%s", name); 2604 } 2605 } else { 2606 opt = attr_to_option(fattr); 2607 if (opt != NULL) { 2608 len = strlen(opt); 2609 rep->exttr[fattr].name = xmalloc(len + 1, rep); 2610 if (val) 2611 (void) strlcpy(rep->exttr[fattr].name, 2612 opt, len + 1); 2613 else 2614 (void) strlcpy(rep->exttr[fattr].name, 2615 "-", len + 1); 2616 } 2617 } 2618 } 2619 } 2620 2621 /* Set extended system attribute timestamp display */ 2622 2623 void 2624 set_sysattrtm_display(char *name, struct lbuf *rep) 2625 { 2626 uint_t nelem; 2627 uint64_t *value; 2628 int i; 2629 size_t len; 2630 2631 if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) { 2632 if (*value != NULL) { 2633 len = strlen(name); 2634 i = 0; 2635 while (rep->extm[i].stm != 0 && i < sacnt) 2636 i++; 2637 rep->extm[i].stm = value[0]; 2638 rep->extm[i].nstm = value[1]; 2639 rep->extm[i].name = xmalloc(len + 1, rep); 2640 (void) strlcpy(rep->extm[i].name, name, len + 1); 2641 } 2642 } 2643 } 2644 2645 void 2646 format_time(time_t sec, time_t nsec) 2647 { 2648 const char *fstr = time_fmt_new; 2649 char fmt_buf[FMTSIZE]; 2650 2651 if (Eflg) { 2652 (void) snprintf(fmt_buf, FMTSIZE, fstr, nsec); 2653 (void) strftime(time_buf, sizeof (time_buf), fmt_buf, 2654 localtime(&sec)); 2655 return; 2656 } 2657 2658 if (sec < year || sec > now) 2659 fstr = time_fmt_old; 2660 2661 /* if a custom time was specified, shouldn't be localized */ 2662 (void) strftime(time_buf, sizeof (time_buf), 2663 (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr, 2664 localtime(&sec)); 2665 } 2666 2667 void 2668 format_attrtime(struct lbuf *p) 2669 { 2670 int tmattr = 0; 2671 int i; 2672 2673 if (p->extm != NULL) { 2674 for (i = 0; i < sacnt; i++) { 2675 if (p->extm[i].name != NULL) { 2676 tmattr = 1; 2677 break; 2678 } 2679 } 2680 } 2681 2682 if (tmattr) { 2683 const char *old_save = time_fmt_old; 2684 const char *new_save = time_fmt_new; 2685 2686 /* Eflg always sets format to FORMAT_ISO_FULL */ 2687 if (!Eflg && !time_custom) { 2688 time_fmt_old = FORMAT_OLD; 2689 time_fmt_new = FORMAT_NEW; 2690 } 2691 2692 format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm); 2693 2694 time_fmt_old = old_save; 2695 time_fmt_new = new_save; 2696 } 2697 } 2698 2699 void 2700 print_time(struct lbuf *p) 2701 { 2702 const char *old_save = time_fmt_old; 2703 const char *new_save = time_fmt_new; 2704 2705 int i = 0; 2706 2707 if (!Eflg) { 2708 time_fmt_old = FORMAT_LONG; 2709 time_fmt_new = FORMAT_LONG; 2710 } 2711 2712 new_line(); 2713 format_time(p->lat.tv_sec, p->lat.tv_nsec); 2714 (void) printf(" timestamp: atime %s\n", time_buf); 2715 format_time(p->lct.tv_sec, p->lct.tv_nsec); 2716 (void) printf(" timestamp: ctime %s\n", time_buf); 2717 format_time(p->lmt.tv_sec, p->lmt.tv_nsec); 2718 (void) printf(" timestamp: mtime %s\n", time_buf); 2719 if (p->extm != NULL) { 2720 while (p->extm[i].nstm != 0 && i < sacnt) { 2721 format_time(p->extm[i].stm, p->extm[i].nstm); 2722 if (p->extm[i].name != NULL) { 2723 (void) printf(" timestamp:" 2724 " %s %s\n", 2725 p->extm[i].name, time_buf); 2726 } 2727 i++; 2728 } 2729 } 2730 2731 time_fmt_old = old_save; 2732 time_fmt_new = new_save; 2733 } 2734 2735 /* 2736 * Check if color definition applies to entry, returns 1 if yes, 0 if no 2737 */ 2738 static int 2739 color_match(const char *fname, mode_t mode, ls_color_t *color) 2740 { 2741 switch (color->ftype) { 2742 case LS_PAT: 2743 { 2744 size_t fname_len, sfx_len; 2745 2746 fname_len = strlen(fname); 2747 sfx_len = strlen(color->sfx); 2748 if (sfx_len > fname_len) 2749 return (0); 2750 2751 if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0) 2752 return (1); 2753 else 2754 return (0); 2755 } 2756 2757 case LS_NORMAL: 2758 return (1); 2759 2760 case LS_FILE: 2761 return (S_ISREG(mode)); 2762 2763 case LS_DIR: 2764 return (S_ISDIR(mode)); 2765 2766 case LS_LINK: 2767 return (S_ISLNK(mode)); 2768 2769 case LS_FIFO: 2770 return (S_ISFIFO(mode)); 2771 2772 case LS_SOCK: 2773 return (S_ISSOCK(mode)); 2774 2775 case LS_DOOR: 2776 return (S_ISDOOR(mode)); 2777 2778 case LS_BLK: 2779 return (S_ISBLK(mode)); 2780 2781 case LS_CHR: 2782 return (S_ISCHR(mode)); 2783 2784 case LS_PORT: 2785 return (S_ISPORT(mode)); 2786 2787 case LS_ORPHAN: 2788 /* this is tested for by gstat */ 2789 return (0); 2790 2791 case LS_SETUID: 2792 return (!S_ISLNK(mode) && (mode & S_ISUID)); 2793 2794 case LS_SETGID: 2795 return (!S_ISLNK(mode) && (mode & S_ISGID)); 2796 2797 case LS_STICKY_OTHER_WRITABLE: 2798 return (!S_ISLNK(mode) && (mode & (S_IWOTH|S_ISVTX))); 2799 2800 case LS_OTHER_WRITABLE: 2801 return (!S_ISLNK(mode) && (mode & S_IWOTH)); 2802 2803 case LS_STICKY: 2804 return (!S_ISLNK(mode) && (mode & S_ISVTX)); 2805 2806 case LS_EXEC: 2807 return (!S_ISLNK(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH))); 2808 } 2809 2810 return (0); 2811 } 2812 2813 static void 2814 dump_color(ls_color_t *c) 2815 { 2816 if (c == NULL) 2817 return; 2818 2819 (void) printf("\n\ttype: "); 2820 switch (c->ftype) { 2821 case LS_NORMAL: 2822 (void) printf("LS_NORMAL"); 2823 break; 2824 case LS_FILE: 2825 (void) printf("LS_FILE"); 2826 break; 2827 case LS_EXEC: 2828 (void) printf("LS_EXEC"); 2829 break; 2830 case LS_DIR: 2831 (void) printf("LS_DIR"); 2832 break; 2833 case LS_LINK: 2834 (void) printf("LS_LINK"); 2835 break; 2836 2837 case LS_FIFO: 2838 (void) printf("LS_FIFO"); 2839 break; 2840 2841 case LS_SOCK: 2842 (void) printf("LS_SOCK"); 2843 break; 2844 2845 case LS_DOOR: 2846 (void) printf("LS_DOOR"); 2847 break; 2848 2849 case LS_BLK: 2850 (void) printf("LS_BLK"); 2851 break; 2852 2853 case LS_CHR: 2854 (void) printf("LS_CHR"); 2855 break; 2856 2857 case LS_PORT: 2858 (void) printf("LS_PORT"); 2859 break; 2860 2861 case LS_STICKY: 2862 (void) printf("LS_STICKY"); 2863 break; 2864 2865 case LS_ORPHAN: 2866 (void) printf("LS_ORPHAN"); 2867 break; 2868 2869 case LS_SETGID: 2870 (void) printf("LS_SETGID"); 2871 break; 2872 2873 case LS_SETUID: 2874 (void) printf("LS_SETUID"); 2875 break; 2876 2877 case LS_OTHER_WRITABLE: 2878 (void) printf("LS_OTHER_WRITABLE"); 2879 break; 2880 2881 case LS_STICKY_OTHER_WRITABLE: 2882 (void) printf("LS_STICKY_OTHER_WRITABLE"); 2883 break; 2884 2885 case LS_PAT: 2886 (void) printf("LS_PAT\n"); 2887 (void) printf("\tpattern: %s", c->sfx); 2888 break; 2889 } 2890 (void) printf("\n"); 2891 (void) printf("\tattr: %d\n", c->attr); 2892 (void) printf("\tfg: %d\n", c->fg); 2893 (void) printf("\tbg: %d\n", c->bg); 2894 (void) printf("\t"); 2895 } 2896 2897 static ls_color_t * 2898 ls_color_find(const char *fname, mode_t mode) 2899 { 2900 int i; 2901 2902 /* 2903 * Colors are sorted from most general lsc_colors[0] to most specific 2904 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with 2905 * most specific color rule and work towards most general. 2906 */ 2907 for (i = lsc_ncolors - 1; i >= 0; --i) 2908 if (color_match(fname, mode, &lsc_colors[i])) 2909 return (&lsc_colors[i]); 2910 2911 return (NULL); 2912 } 2913 2914 static void 2915 ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4, 2916 long int p5, long int p6, long int p7, long int p8, long int p9) 2917 { 2918 char *s; 2919 2920 if (str == NULL) 2921 return; 2922 2923 s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9); 2924 2925 if (s != NULL) 2926 (void) putp(s); 2927 } 2928 2929 static void 2930 ls_start_color(ls_color_t *c) 2931 { 2932 if (c == NULL) 2933 return; 2934 2935 if (lsc_debug) 2936 lsc_match = c; 2937 2938 if (c->attr & LSA_BOLD) 2939 ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2940 if (c->attr & LSA_UNDERSCORE) 2941 ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2942 if (c->attr & LSA_BLINK) 2943 ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2944 if (c->attr & LSA_REVERSE) 2945 ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2946 if (c->attr & LSA_CONCEALED) 2947 ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2948 if (c->attr == LSA_NONE) 2949 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2950 2951 if (c->fg != -1) 2952 ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0); 2953 if (c->bg != -1) 2954 ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0); 2955 } 2956 2957 static void 2958 ls_end_color() 2959 { 2960 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2961 if (lsc_debug) 2962 dump_color(lsc_match); 2963 } 2964 2965 static void 2966 new_color_entry(char *colorstr) 2967 { 2968 static const struct { 2969 const char *s; 2970 ls_cftype_t stype; 2971 } type_map[] = { 2972 { "no", LS_NORMAL }, 2973 { "fi", LS_FILE }, 2974 { "di", LS_DIR }, 2975 { "ln", LS_LINK }, 2976 { "pi", LS_FIFO }, 2977 { "so", LS_SOCK }, 2978 { "do", LS_DOOR }, 2979 { "bd", LS_BLK }, 2980 { "cd", LS_CHR }, 2981 { "or", LS_ORPHAN }, 2982 { "su", LS_SETUID }, 2983 { "sg", LS_SETGID }, 2984 { "tw", LS_STICKY_OTHER_WRITABLE }, 2985 { "ow", LS_OTHER_WRITABLE }, 2986 { "st", LS_STICKY }, 2987 { "ex", LS_EXEC }, 2988 { "po", LS_PORT }, 2989 { NULL, LS_NORMAL } 2990 }; 2991 2992 char *p, *lasts; 2993 int i; 2994 int color, attr; 2995 2996 p = strtok_r(colorstr, "=", &lasts); 2997 if (p == NULL) { 2998 colorflg = 0; 2999 return; 3000 } 3001 3002 if (p[0] == '*') { 3003 lsc_colors[lsc_ncolors].ftype = LS_PAT; 3004 /* don't include the * in the suffix */ 3005 if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) { 3006 colorflg = 0; 3007 return; 3008 } 3009 } else { 3010 lsc_colors[lsc_ncolors].sfx = NULL; 3011 3012 for (i = 0; type_map[i].s != NULL; ++i) { 3013 if (strncmp(type_map[i].s, p, 2) == 0) 3014 break; 3015 } 3016 3017 /* ignore unknown file types */ 3018 if (type_map[i].s == NULL) 3019 return; 3020 3021 lsc_colors[lsc_ncolors].ftype = type_map[i].stype; 3022 } 3023 3024 attr = LSA_NONE; 3025 lsc_colors[lsc_ncolors].fg = -1; 3026 lsc_colors[lsc_ncolors].bg = -1; 3027 for (p = strtok_r(NULL, ";", &lasts); p != NULL; 3028 p = strtok_r(NULL, ";", &lasts)) { 3029 color = strtol(p, NULL, 10); 3030 3031 if (color < 10) { 3032 switch (color) { 3033 case 0: 3034 attr = LSA_NONE; 3035 continue; 3036 case 1: 3037 attr |= LSA_BOLD; 3038 continue; 3039 case 4: 3040 attr |= LSA_UNDERSCORE; 3041 continue; 3042 case 5: 3043 attr |= LSA_BLINK; 3044 continue; 3045 case 7: 3046 attr |= LSA_REVERSE; 3047 continue; 3048 case 8: 3049 attr |= LSA_CONCEALED; 3050 continue; 3051 default: 3052 continue; 3053 } 3054 } 3055 3056 if (color < 40) 3057 lsc_colors[lsc_ncolors].fg = color - 30; 3058 else 3059 lsc_colors[lsc_ncolors].bg = color - 40; 3060 } 3061 3062 lsc_colors[lsc_ncolors].attr = attr; 3063 ++lsc_ncolors; 3064 } 3065 3066 static int 3067 ls_color_compare(const void *p1, const void *p2) 3068 { 3069 const ls_color_t *c1 = (const ls_color_t *)p1; 3070 const ls_color_t *c2 = (const ls_color_t *)p2; 3071 3072 int ret = c1->ftype - c2->ftype; 3073 3074 if (ret != 0) 3075 return (ret); 3076 3077 if (c1->ftype != LS_PAT) 3078 return (ret); 3079 3080 return (strcmp(c1->sfx, c2->sfx)); 3081 } 3082 3083 static void 3084 ls_color_init() 3085 { 3086 static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35" 3087 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01" 3088 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31" 3089 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31" 3090 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31" 3091 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35" 3092 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35" 3093 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35" 3094 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35" 3095 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35" 3096 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35"; 3097 3098 char *colorstr; 3099 char *p, *lasts; 3100 size_t color_sz; 3101 int termret; 3102 int i; 3103 3104 (void) setupterm(NULL, 1, &termret); 3105 if (termret != 1) 3106 return; 3107 3108 if ((p = getenv("LS_COLORS")) == NULL) 3109 p = default_colorstr; 3110 colorstr = strdup(p); 3111 if (colorstr == NULL) 3112 return; 3113 3114 /* 3115 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors 3116 * if there are invalid entries passed in the string (they are ignored) 3117 */ 3118 color_sz = 1; 3119 for (p = strchr(colorstr, ':'); p != NULL && *p != '\0'; 3120 p = strchr(++p, ':')) 3121 ++color_sz; 3122 3123 lsc_colors = calloc(color_sz, sizeof (ls_color_t)); 3124 if (lsc_colors == NULL) { 3125 free(colorstr); 3126 return; 3127 } 3128 3129 for (p = strtok_r(colorstr, ":", &lasts); 3130 p != NULL && lsc_ncolors < color_sz; 3131 p = strtok_r(NULL, ":", &lasts)) 3132 new_color_entry(p); 3133 3134 qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t), 3135 ls_color_compare); 3136 3137 for (i = 0; i < lsc_ncolors; ++i) 3138 if (lsc_colors[i].ftype == LS_ORPHAN) { 3139 lsc_orphan = &lsc_colors[i]; 3140 break; 3141 } 3142 3143 if ((lsc_bold = tigetstr("bold")) == (char *)-1) 3144 lsc_bold = NULL; 3145 3146 if ((lsc_underline = tigetstr("smul")) == (char *)-1) 3147 lsc_underline = NULL; 3148 3149 if ((lsc_blink = tigetstr("blink")) == (char *)-1) 3150 lsc_blink = NULL; 3151 3152 if ((lsc_reverse = tigetstr("rev")) == (char *)-1) 3153 lsc_reverse = NULL; 3154 3155 if ((lsc_concealed = tigetstr("prot")) == (char *)-1) 3156 lsc_concealed = NULL; 3157 3158 if ((lsc_none = tigetstr("sgr0")) == (char *)-1) 3159 lsc_none = NULL; 3160 3161 if ((lsc_setfg = tigetstr("setaf")) == (char *)-1) 3162 lsc_setfg = NULL; 3163 3164 if ((lsc_setbg = tigetstr("setab")) == (char *)-1) 3165 lsc_setbg = NULL; 3166 3167 if (getenv("_LS_COLOR_DEBUG") != NULL) { 3168 int i; 3169 3170 lsc_debug = 1; 3171 for (i = 0; i < lsc_ncolors; ++i) 3172 dump_color(&lsc_colors[i]); 3173 } 3174 3175 free(colorstr); 3176 } 3177 3178 /* Free extended system attribute lists */ 3179 3180 void 3181 free_sysattr(struct lbuf *p) 3182 { 3183 int i; 3184 3185 if (p->exttr != NULL) { 3186 for (i = 0; i < sacnt; i++) { 3187 if (p->exttr[i].name != NULL) 3188 free(p->exttr[i].name); 3189 } 3190 free(p->exttr); 3191 } 3192 if (p->extm != NULL) { 3193 for (i = 0; i < sacnt; i++) { 3194 if (p->extm[i].name != NULL) 3195 free(p->extm[i].name); 3196 } 3197 free(p->extm); 3198 } 3199 } 3200 3201 /* Allocate extended system attribute list */ 3202 3203 void * 3204 xmalloc(size_t size, struct lbuf *p) 3205 { 3206 if ((p = malloc(size)) == NULL) { 3207 perror("ls"); 3208 free_sysattr(p); 3209 nvlist_free(response); 3210 exit(2); 3211 } 3212 return (p); 3213 } 3214