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