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