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