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