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