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