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 isatty(1) == 1) { 460 colorflg++; 461 statreq++; 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("Out of memory"); 653 exit(1); 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(acl_t *acep) 1680 { 1681 int mode = 0, seen = 0; 1682 int acecnt; 1683 int flags; 1684 ace_t *ap; 1685 1686 acecnt = acl_cnt(acep); 1687 for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) { 1688 1689 if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE && 1690 ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE) 1691 continue; 1692 1693 if (ap->a_flags & ACE_INHERIT_ONLY_ACE) 1694 continue; 1695 1696 /* 1697 * if it is first group@ or first everyone@ 1698 * for each of read, write and execute, then 1699 * that will be the group mode bit. 1700 */ 1701 flags = ap->a_flags & ACE_TYPE_FLAGS; 1702 if (flags == OWNED_GROUP || flags == ACE_EVERYONE) { 1703 if (ap->a_access_mask & ACE_READ_DATA) { 1704 if (!(seen & S_IRGRP)) { 1705 seen |= S_IRGRP; 1706 if (IS_TYPE_ALLOWED(ap->a_type)) 1707 mode |= S_IRGRP; 1708 } 1709 } 1710 if (ap->a_access_mask & ACE_WRITE_DATA) { 1711 if (!(seen & S_IWGRP)) { 1712 seen |= S_IWGRP; 1713 if (IS_TYPE_ALLOWED(ap->a_type)) 1714 mode |= S_IWGRP; 1715 } 1716 } 1717 if (ap->a_access_mask & ACE_EXECUTE) { 1718 if (!(seen & S_IXGRP)) { 1719 seen |= S_IXGRP; 1720 if (IS_TYPE_ALLOWED(ap->a_type)) 1721 mode |= S_IXGRP; 1722 } 1723 } 1724 } 1725 } 1726 return (mode); 1727 } 1728 1729 /* 1730 * get status of file and recomputes tblocks; 1731 * argfl = 1 if file is a name in ls-command and = 0 1732 * for filename in a directory whose name is an 1733 * argument in the command; 1734 * stores a pointer in flist[nfiles] and 1735 * returns that pointer; 1736 * returns NULL if failed; 1737 */ 1738 static struct lbuf * 1739 gstat(char *file, int argfl, struct ditem *myparent) 1740 { 1741 struct stat statb, statb1; 1742 struct lbuf *rep; 1743 char buf[BUFSIZ]; 1744 ssize_t cc; 1745 int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat; 1746 int aclcnt; 1747 int error; 1748 aclent_t *tp; 1749 o_mode_t groupperm, mask; 1750 int grouppermfound, maskfound; 1751 1752 if (nomocore) 1753 return (NULL); 1754 1755 if (nfiles >= maxfils) { 1756 /* 1757 * all flist/lbuf pair assigned files, time to get some 1758 * more space 1759 */ 1760 maxfils += quantn; 1761 if (((flist = realloc(flist, 1762 maxfils * sizeof (struct lbuf *))) == NULL) || 1763 ((nxtlbf = malloc(quantn * 1764 sizeof (struct lbuf))) == NULL)) { 1765 perror("ls"); 1766 nomocore = 1; 1767 return (NULL); 1768 } 1769 } 1770 1771 /* 1772 * nfiles is reset to nargs for each directory 1773 * that is given as an argument maxn is checked 1774 * to prevent the assignment of an lbuf to a flist entry 1775 * that already has one assigned. 1776 */ 1777 if (nfiles >= maxn) { 1778 rep = nxtlbf++; 1779 flist[nfiles++] = rep; 1780 maxn = nfiles; 1781 } else { 1782 rep = flist[nfiles++]; 1783 } 1784 1785 /* Initialize */ 1786 1787 rep->lflags = (mode_t)0; 1788 rep->flinkto = NULL; 1789 rep->cycle = 0; 1790 rep->lat.tv_sec = time(NULL); 1791 rep->lat.tv_nsec = 0; 1792 rep->lct.tv_sec = time(NULL); 1793 rep->lct.tv_nsec = 0; 1794 rep->lmt.tv_sec = time(NULL); 1795 rep->lmt.tv_nsec = 0; 1796 rep->exttr = NULL; 1797 rep->extm = NULL; 1798 rep->color = NULL; 1799 rep->link_color = NULL; 1800 1801 if (argfl || statreq) { 1802 int doacl; 1803 1804 if (lflg) 1805 doacl = 1; 1806 else 1807 doacl = 0; 1808 1809 if ((*statf)(file, &statb) < 0) { 1810 if (argfl || errno != ENOENT || 1811 (Lflg && lstat(file, &statb) == 0)) { 1812 /* 1813 * Avoid race between readdir and lstat. 1814 * Print error message in case of dangling link. 1815 */ 1816 perror(file); 1817 err = 2; 1818 } 1819 nfiles--; 1820 return (NULL); 1821 } 1822 1823 /* 1824 * If -H was specified, and the file linked to was 1825 * not a directory, then we need to get the info 1826 * for the symlink itself. 1827 */ 1828 if ((Hflg) && (argfl) && 1829 ((statb.st_mode & S_IFMT) != S_IFDIR)) { 1830 if (lstat(file, &statb) < 0) { 1831 perror(file); 1832 err = 2; 1833 } 1834 } 1835 1836 rep->lnum = statb.st_ino; 1837 rep->lsize = statb.st_size; 1838 rep->lblocks = statb.st_blocks; 1839 if (colorflg) 1840 rep->color = ls_color_find(file, statb.st_mode); 1841 1842 switch (statb.st_mode & S_IFMT) { 1843 case S_IFDIR: 1844 rep->ltype = 'd'; 1845 if (Rflg) { 1846 record_ancestry(file, &statb, rep, 1847 argfl, myparent); 1848 } 1849 break; 1850 case S_IFBLK: 1851 rep->ltype = 'b'; 1852 rep->lsize = (off_t)statb.st_rdev; 1853 break; 1854 case S_IFCHR: 1855 rep->ltype = 'c'; 1856 rep->lsize = (off_t)statb.st_rdev; 1857 break; 1858 case S_IFIFO: 1859 rep->ltype = 'p'; 1860 break; 1861 case S_IFSOCK: 1862 rep->ltype = 's'; 1863 rep->lsize = 0; 1864 break; 1865 case S_IFLNK: 1866 /* symbolic links may not have ACLs, so elide acl() */ 1867 if ((Lflg == 0) || (Hflg == 0) || 1868 ((Hflg) && (!argfl))) { 1869 doacl = 0; 1870 } 1871 rep->ltype = 'l'; 1872 if (lflg || colorflg) { 1873 cc = readlink(file, buf, BUFSIZ); 1874 if (cc < 0) 1875 break; 1876 1877 /* 1878 * follow the symbolic link 1879 * to generate the appropriate 1880 * Fflg marker for the object 1881 * eg, /bin -> /sym/bin/ 1882 */ 1883 error = 0; 1884 if (Fflg || pflg || colorflg) 1885 error = stat(file, &statb1); 1886 1887 if (colorflg) { 1888 if (error >= 0) 1889 rep->link_color = 1890 ls_color_find(file, 1891 statb1.st_mode); 1892 else 1893 rep->link_color = 1894 lsc_orphan; 1895 } 1896 1897 if ((Fflg || pflg) && error >= 0) { 1898 switch (statb1.st_mode & S_IFMT) { 1899 case S_IFDIR: 1900 buf[cc++] = '/'; 1901 break; 1902 case S_IFSOCK: 1903 buf[cc++] = '='; 1904 break; 1905 case S_IFDOOR: 1906 buf[cc++] = '>'; 1907 break; 1908 case S_IFIFO: 1909 buf[cc++] = '|'; 1910 break; 1911 default: 1912 if ((statb1.st_mode & ~S_IFMT) & 1913 (S_IXUSR|S_IXGRP| S_IXOTH)) 1914 buf[cc++] = '*'; 1915 break; 1916 } 1917 } 1918 buf[cc] = '\0'; 1919 rep->flinkto = strdup(buf); 1920 break; 1921 } 1922 1923 /* 1924 * ls /sym behaves differently from ls /sym/ 1925 * when /sym is a symbolic link. This is fixed 1926 * when explicit arguments are specified. 1927 */ 1928 1929 #ifdef XPG6 1930 /* Do not follow a symlink when -F is specified */ 1931 if ((!argfl) || (argfl && Fflg) || 1932 (stat(file, &statb1) < 0)) 1933 #else 1934 /* Follow a symlink when -F is specified */ 1935 if (!argfl || stat(file, &statb1) < 0) 1936 #endif /* XPG6 */ 1937 break; 1938 if ((statb1.st_mode & S_IFMT) == S_IFDIR) { 1939 statb = statb1; 1940 rep->ltype = 'd'; 1941 rep->lsize = statb1.st_size; 1942 if (Rflg) { 1943 record_ancestry(file, &statb, rep, 1944 argfl, myparent); 1945 } 1946 } 1947 break; 1948 case S_IFDOOR: 1949 rep->ltype = 'D'; 1950 break; 1951 case S_IFREG: 1952 rep->ltype = '-'; 1953 break; 1954 case S_IFPORT: 1955 rep->ltype = 'P'; 1956 break; 1957 default: 1958 rep->ltype = '?'; 1959 break; 1960 } 1961 rep->lflags = statb.st_mode & ~S_IFMT; 1962 1963 if (!S_ISREG(statb.st_mode)) 1964 rep->lflags |= LS_NOTREG; 1965 1966 rep->luid = statb.st_uid; 1967 rep->lgid = statb.st_gid; 1968 rep->lnl = statb.st_nlink; 1969 if (uflg || (tmflg && atm)) 1970 rep->lmtime = statb.st_atim; 1971 else if (cflg || (tmflg && ctm)) 1972 rep->lmtime = statb.st_ctim; 1973 else 1974 rep->lmtime = statb.st_mtim; 1975 rep->lat = statb.st_atim; 1976 rep->lct = statb.st_ctim; 1977 rep->lmt = statb.st_mtim; 1978 1979 /* ACL: check acl entries count */ 1980 if (doacl) { 1981 1982 error = acl_get(file, 0, &rep->aclp); 1983 if (error) { 1984 (void) fprintf(stderr, 1985 gettext("ls: can't read ACL on %s: %s\n"), 1986 file, acl_strerror(error)); 1987 rep->acl = ' '; 1988 acl_err++; 1989 return (rep); 1990 } 1991 1992 rep->acl = ' '; 1993 1994 if (rep->aclp && 1995 ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) { 1996 rep->acl = '+'; 1997 /* 1998 * Special handling for ufs aka aclent_t ACL's 1999 */ 2000 if (acl_type(rep->aclp) == ACLENT_T) { 2001 /* 2002 * For files with non-trivial acls, the 2003 * effective group permissions are the 2004 * intersection of the GROUP_OBJ value 2005 * and the CLASS_OBJ (acl mask) value. 2006 * Determine both the GROUP_OBJ and 2007 * CLASS_OBJ for this file and insert 2008 * the logical AND of those two values 2009 * in the group permissions field 2010 * of the lflags value for this file. 2011 */ 2012 2013 /* 2014 * Until found in acl list, assume 2015 * maximum permissions for both group 2016 * a nd mask. (Just in case the acl 2017 * lacks either value for some reason.) 2018 */ 2019 groupperm = 07; 2020 mask = 07; 2021 grouppermfound = 0; 2022 maskfound = 0; 2023 aclcnt = acl_cnt(rep->aclp); 2024 for (tp = 2025 (aclent_t *)acl_data(rep->aclp); 2026 aclcnt--; tp++) { 2027 if (tp->a_type == GROUP_OBJ) { 2028 groupperm = tp->a_perm; 2029 grouppermfound = 1; 2030 continue; 2031 } 2032 if (tp->a_type == CLASS_OBJ) { 2033 mask = tp->a_perm; 2034 maskfound = 1; 2035 } 2036 if (grouppermfound && maskfound) 2037 break; 2038 } 2039 2040 2041 /* reset all the group bits */ 2042 rep->lflags &= ~S_IRWXG; 2043 2044 /* 2045 * Now set them to the logical AND of 2046 * the GROUP_OBJ permissions and the 2047 * acl mask. 2048 */ 2049 2050 rep->lflags |= (groupperm & mask) << 3; 2051 2052 } else if (acl_type(rep->aclp) == ACE_T) { 2053 int mode; 2054 mode = grp_mask_to_mode(rep->aclp); 2055 rep->lflags &= ~S_IRWXG; 2056 rep->lflags |= mode; 2057 } 2058 } 2059 2060 if (!vflg && !Vflg && rep->aclp) { 2061 acl_free(rep->aclp); 2062 rep->aclp = NULL; 2063 } 2064 2065 if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1) 2066 rep->acl = '@'; 2067 2068 } else 2069 rep->acl = ' '; 2070 2071 /* mask ISARG and other file-type bits */ 2072 2073 if (rep->ltype != 'b' && rep->ltype != 'c') 2074 tblocks += rep->lblocks; 2075 2076 /* Get extended system attributes */ 2077 2078 if ((saflg || (tmflg && crtm) || (tmflg && alltm)) && 2079 (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) { 2080 int i; 2081 2082 sacnt = attr_count(); 2083 /* 2084 * Allocate 'sacnt' size array to hold extended 2085 * system attribute name (verbose) or respective 2086 * symbol represenation (compact). 2087 */ 2088 rep->exttr = xmalloc(sacnt * sizeof (struct attrb), 2089 rep); 2090 2091 /* initialize boolean attribute list */ 2092 for (i = 0; i < sacnt; i++) 2093 rep->exttr[i].name = NULL; 2094 if (get_sysxattr(file, rep) != 0) { 2095 (void) fprintf(stderr, 2096 gettext("ls:Failed to retrieve " 2097 "extended system attribute from " 2098 "%s\n"), file); 2099 rep->exttr[0].name = xmalloc(2, rep); 2100 (void) strlcpy(rep->exttr[0].name, "?", 2); 2101 } 2102 } 2103 } 2104 return (rep); 2105 } 2106 2107 /* 2108 * returns pathname of the form dir/file; 2109 * dir and file are null-terminated strings. 2110 */ 2111 static char * 2112 makename(char *dir, char *file) 2113 { 2114 /* 2115 * PATH_MAX is the maximum length of a path name. 2116 * MAXNAMLEN is the maximum length of any path name component. 2117 * Allocate space for both, plus the '/' in the middle 2118 * and the null character at the end. 2119 * dfile is static as this is returned by makename(). 2120 */ 2121 static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1]; 2122 char *dp, *fp; 2123 2124 dp = dfile; 2125 fp = dir; 2126 while (*fp) 2127 *dp++ = *fp++; 2128 if (dp > dfile && *(dp - 1) != '/') 2129 *dp++ = '/'; 2130 fp = file; 2131 while (*fp) 2132 *dp++ = *fp++; 2133 *dp = '\0'; 2134 return (dfile); 2135 } 2136 2137 2138 #include <pwd.h> 2139 #include <grp.h> 2140 #include <utmpx.h> 2141 2142 struct utmpx utmp; 2143 2144 #define NMAX (sizeof (utmp.ut_name)) 2145 #define SCPYN(a, b) (void) strncpy(a, b, NMAX) 2146 2147 2148 struct cachenode { /* this struct must be zeroed before using */ 2149 struct cachenode *lesschild; /* subtree whose entries < val */ 2150 struct cachenode *grtrchild; /* subtree whose entries > val */ 2151 long val; /* the uid or gid of this entry */ 2152 int initted; /* name has been filled in */ 2153 char name[NMAX+1]; /* the string that val maps to */ 2154 }; 2155 static struct cachenode *names, *groups; 2156 2157 static struct cachenode * 2158 findincache(struct cachenode **head, long val) 2159 { 2160 struct cachenode **parent = head; 2161 struct cachenode *c = *parent; 2162 2163 while (c != NULL) { 2164 if (val == c->val) { 2165 /* found it */ 2166 return (c); 2167 } else if (val < c->val) { 2168 parent = &c->lesschild; 2169 c = c->lesschild; 2170 } else { 2171 parent = &c->grtrchild; 2172 c = c->grtrchild; 2173 } 2174 } 2175 2176 /* not in the cache, make a new entry for it */ 2177 c = calloc(1, sizeof (struct cachenode)); 2178 if (c == NULL) { 2179 perror("ls"); 2180 exit(2); 2181 } 2182 *parent = c; 2183 c->val = val; 2184 return (c); 2185 } 2186 2187 /* 2188 * get name from cache, or passwd file for a given uid; 2189 * lastuid is set to uid. 2190 */ 2191 static char * 2192 getname(uid_t uid) 2193 { 2194 struct passwd *pwent; 2195 struct cachenode *c; 2196 2197 if ((uid == lastuid) && lastuname) 2198 return (lastuname); 2199 2200 c = findincache(&names, uid); 2201 if (c->initted == 0) { 2202 if ((pwent = getpwuid(uid)) != NULL) { 2203 SCPYN(&c->name[0], pwent->pw_name); 2204 } else { 2205 (void) sprintf(&c->name[0], "%-8u", (int)uid); 2206 } 2207 c->initted = 1; 2208 } 2209 lastuid = uid; 2210 lastuname = &c->name[0]; 2211 return (lastuname); 2212 } 2213 2214 /* 2215 * get name from cache, or group file for a given gid; 2216 * lastgid is set to gid. 2217 */ 2218 static char * 2219 getgroup(gid_t gid) 2220 { 2221 struct group *grent; 2222 struct cachenode *c; 2223 2224 if ((gid == lastgid) && lastgname) 2225 return (lastgname); 2226 2227 c = findincache(&groups, gid); 2228 if (c->initted == 0) { 2229 if ((grent = getgrgid(gid)) != NULL) { 2230 SCPYN(&c->name[0], grent->gr_name); 2231 } else { 2232 (void) sprintf(&c->name[0], "%-8u", (int)gid); 2233 } 2234 c->initted = 1; 2235 } 2236 lastgid = gid; 2237 lastgname = &c->name[0]; 2238 return (lastgname); 2239 } 2240 2241 /* return >0 if item pointed by pp2 should appear first */ 2242 static int 2243 compar(struct lbuf **pp1, struct lbuf **pp2) 2244 { 2245 struct lbuf *p1, *p2; 2246 2247 p1 = *pp1; 2248 p2 = *pp2; 2249 if (dflg == 0) { 2250 /* 2251 * compare two names in ls-command one of which is file 2252 * and the other is a directory; 2253 * this portion is not used for comparing files within 2254 * a directory name of ls-command; 2255 */ 2256 if (p1->lflags&ISARG && p1->ltype == 'd') { 2257 if (!(p2->lflags&ISARG && p2->ltype == 'd')) 2258 return (1); 2259 } else { 2260 if (p2->lflags&ISARG && p2->ltype == 'd') 2261 return (-1); 2262 } 2263 } 2264 if (tflg) { 2265 if (p2->lmtime.tv_sec > p1->lmtime.tv_sec) 2266 return (rflg); 2267 else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec) 2268 return (-rflg); 2269 /* times are equal to the sec, check nsec */ 2270 if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec) 2271 return (rflg); 2272 else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec) 2273 return (-rflg); 2274 /* if times are equal, fall through and sort by name */ 2275 } else if (Sflg) { 2276 /* 2277 * The size stored in lsize can be either the 2278 * size or the major minor number (in the case of 2279 * block and character special devices). If it's 2280 * a major minor number, then the size is considered 2281 * to be zero and we want to fall through and sort 2282 * by name. In addition, if the size of p2 is equal 2283 * to the size of p1 we want to fall through and 2284 * sort by name. 2285 */ 2286 off_t p1size = (p1->ltype == 'b') || 2287 (p1->ltype == 'c') ? 0 : p1->lsize; 2288 off_t p2size = (p2->ltype == 'b') || 2289 (p2->ltype == 'c') ? 0 : p2->lsize; 2290 if (p2size > p1size) { 2291 return (rflg); 2292 } else if (p2size < p1size) { 2293 return (-rflg); 2294 } 2295 /* Sizes are equal, fall through and sort by name. */ 2296 } 2297 return (rflg * strcoll( 2298 p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname, 2299 p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname)); 2300 } 2301 2302 static void 2303 pprintf(char *s1, char *s2) 2304 { 2305 csi_pprintf((unsigned char *)s1); 2306 csi_pprintf((unsigned char *)s2); 2307 } 2308 2309 static void 2310 csi_pprintf(unsigned char *s) 2311 { 2312 unsigned char *cp; 2313 char c; 2314 int i; 2315 int c_len; 2316 int p_col; 2317 wchar_t pcode; 2318 2319 if (!qflg && !bflg) { 2320 for (cp = s; *cp != '\0'; cp++) { 2321 (void) putchar(*cp); 2322 curcol++; 2323 } 2324 return; 2325 } 2326 2327 for (cp = s; *cp; ) { 2328 if (isascii(c = *cp)) { 2329 if (!isprint(c)) { 2330 if (qflg) { 2331 c = '?'; 2332 } else { 2333 curcol += 3; 2334 (void) putc('\\', stdout); 2335 c = '0' + ((*cp >> 6) & 07); 2336 (void) putc(c, stdout); 2337 c = '0' + ((*cp >> 3) & 07); 2338 (void) putc(c, stdout); 2339 c = '0' + (*cp & 07); 2340 } 2341 } 2342 curcol++; 2343 cp++; 2344 (void) putc(c, stdout); 2345 continue; 2346 } 2347 2348 if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) { 2349 c_len = 1; 2350 goto not_print; 2351 } 2352 2353 if ((p_col = wcwidth(pcode)) > 0) { 2354 (void) putwchar(pcode); 2355 cp += c_len; 2356 curcol += p_col; 2357 continue; 2358 } 2359 2360 not_print: 2361 for (i = 0; i < c_len; i++) { 2362 if (qflg) { 2363 c = '?'; 2364 } else { 2365 curcol += 3; 2366 (void) putc('\\', stdout); 2367 c = '0' + ((*cp >> 6) & 07); 2368 (void) putc(c, stdout); 2369 c = '0' + ((*cp >> 3) & 07); 2370 (void) putc(c, stdout); 2371 c = '0' + (*cp & 07); 2372 } 2373 curcol++; 2374 (void) putc(c, stdout); 2375 cp++; 2376 } 2377 } 2378 } 2379 2380 static int 2381 strcol(unsigned char *s1) 2382 { 2383 int w; 2384 int w_col; 2385 int len; 2386 wchar_t wc; 2387 2388 w = 0; 2389 while (*s1) { 2390 if (isascii(*s1)) { 2391 w++; 2392 s1++; 2393 continue; 2394 } 2395 2396 if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) { 2397 w++; 2398 s1++; 2399 continue; 2400 } 2401 2402 if ((w_col = wcwidth(wc)) < 0) 2403 w_col = len; 2404 s1 += len; 2405 w += w_col; 2406 } 2407 return (w); 2408 } 2409 2410 /* 2411 * Convert an unsigned long long to a string representation and place the 2412 * result in the caller-supplied buffer. 2413 * 2414 * The number provided is a size in bytes. The number is first 2415 * converted to an integral multiple of 'scale' bytes. This new 2416 * number is then scaled down until it is small enough to be in a good 2417 * human readable format, i.e. in the range 0 thru scale-1. If the 2418 * number used to derive the final number is not a multiple of scale, and 2419 * the final number has only a single significant digit, we compute 2420 * tenths of units to provide a second significant digit. 2421 * 2422 * The value "(unsigned long long)-1" is a special case and is always 2423 * converted to "-1". 2424 * 2425 * A pointer to the caller-supplied buffer is returned. 2426 */ 2427 static char * 2428 number_to_scaled_string( 2429 numbuf_t buf, /* put the result here */ 2430 unsigned long long number, /* convert this number */ 2431 long scale) 2432 { 2433 unsigned long long save; 2434 /* Measurement: kilo, mega, giga, tera, peta, exa */ 2435 char *uom = "KMGTPE"; 2436 2437 if ((long long)number == (long long)-1) { 2438 (void) strlcpy(buf, "-1", sizeof (numbuf_t)); 2439 return (buf); 2440 } 2441 2442 save = number; 2443 number = number / scale; 2444 2445 /* 2446 * Now we have number as a count of scale units. 2447 * If no further scaling is necessary, we round up as appropriate. 2448 * 2449 * The largest value number could have had entering the routine is 2450 * 16 Exabytes, so running off the end of the uom array should 2451 * never happen. We check for that, though, as a guard against 2452 * a breakdown elsewhere in the algorithm. 2453 */ 2454 if (number < (unsigned long long)scale) { 2455 if ((save % scale) >= (unsigned long long)(scale / 2)) { 2456 if (++number == (unsigned long long)scale) { 2457 uom++; 2458 number = 1; 2459 } 2460 } 2461 } else { 2462 while ((number >= (unsigned long long)scale) && (*uom != 'E')) { 2463 uom++; /* next unit of measurement */ 2464 save = number; 2465 /* 2466 * If we're over half way to the next unit of 2467 * 'scale' bytes (which means we should round 2468 * up), then adding half of 'scale' prior to 2469 * the division will push us into that next 2470 * unit of scale when we perform the division 2471 */ 2472 number = (number + (scale / 2)) / scale; 2473 } 2474 } 2475 2476 /* check if we should output a decimal place after the point */ 2477 if ((save / scale) < 10) { 2478 /* snprintf() will round for us */ 2479 float fnum = (float)save / scale; 2480 (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c", 2481 fnum, *uom); 2482 } else { 2483 (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c", 2484 number, *uom); 2485 } 2486 return (buf); 2487 } 2488 2489 /* Get extended system attributes and set the display */ 2490 2491 int 2492 get_sysxattr(char *fname, struct lbuf *rep) 2493 { 2494 boolean_t value; 2495 data_type_t type; 2496 int error; 2497 char *name; 2498 int i; 2499 2500 if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname, 2501 &response)) != 0) { 2502 perror("ls:getattrat"); 2503 return (error); 2504 } 2505 2506 /* 2507 * Allocate 'sacnt' size array to hold extended timestamp 2508 * system attributes and initialize the array. 2509 */ 2510 rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep); 2511 for (i = 0; i < sacnt; i++) { 2512 rep->extm[i].stm = 0; 2513 rep->extm[i].nstm = 0; 2514 rep->extm[i].name = NULL; 2515 } 2516 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) { 2517 name = nvpair_name(pair); 2518 type = nvpair_type(pair); 2519 if (type == DATA_TYPE_BOOLEAN_VALUE) { 2520 error = nvpair_value_boolean_value(pair, &value); 2521 if (error) { 2522 (void) fprintf(stderr, 2523 gettext("nvpair_value_boolean_value " 2524 "failed: error = %d\n"), error); 2525 continue; 2526 } 2527 if (name != NULL) 2528 set_sysattrb_display(name, value, rep); 2529 continue; 2530 } else if (type == DATA_TYPE_UINT64_ARRAY) { 2531 if (name != NULL) 2532 set_sysattrtm_display(name, rep); 2533 continue; 2534 } 2535 } 2536 nvlist_free(response); 2537 return (0); 2538 } 2539 2540 /* Set extended system attribute boolean display */ 2541 2542 void 2543 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep) 2544 { 2545 f_attr_t fattr; 2546 const char *opt; 2547 size_t len; 2548 2549 fattr = name_to_attr(name); 2550 if (fattr != F_ATTR_INVAL && fattr < sacnt) { 2551 if (vopt) { 2552 len = strlen(name); 2553 if (val) { 2554 rep->exttr[fattr].name = xmalloc(len + 1, rep); 2555 (void) strlcpy(rep->exttr[fattr].name, name, 2556 len + 1); 2557 } else { 2558 rep->exttr[fattr].name = xmalloc(len + 3, rep); 2559 (void) snprintf(rep->exttr[fattr].name, len + 3, 2560 "no%s", name); 2561 } 2562 } else { 2563 opt = attr_to_option(fattr); 2564 if (opt != NULL) { 2565 len = strlen(opt); 2566 rep->exttr[fattr].name = xmalloc(len + 1, rep); 2567 if (val) 2568 (void) strlcpy(rep->exttr[fattr].name, 2569 opt, len + 1); 2570 else 2571 (void) strlcpy(rep->exttr[fattr].name, 2572 "-", len + 1); 2573 } 2574 } 2575 } 2576 } 2577 2578 /* Set extended system attribute timestamp display */ 2579 2580 void 2581 set_sysattrtm_display(char *name, struct lbuf *rep) 2582 { 2583 uint_t nelem; 2584 uint64_t *value; 2585 int i; 2586 size_t len; 2587 2588 if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) { 2589 if (*value != NULL) { 2590 len = strlen(name); 2591 i = 0; 2592 while (rep->extm[i].stm != 0 && i < sacnt) 2593 i++; 2594 rep->extm[i].stm = value[0]; 2595 rep->extm[i].nstm = value[1]; 2596 rep->extm[i].name = xmalloc(len + 1, rep); 2597 (void) strlcpy(rep->extm[i].name, name, len + 1); 2598 } 2599 } 2600 } 2601 2602 void 2603 format_time(time_t sec, time_t nsec) 2604 { 2605 const char *fstr = time_fmt_new; 2606 char fmt_buf[FMTSIZE]; 2607 2608 if (Eflg) { 2609 (void) snprintf(fmt_buf, FMTSIZE, fstr, nsec); 2610 (void) strftime(time_buf, sizeof (time_buf), fmt_buf, 2611 localtime(&sec)); 2612 return; 2613 } 2614 2615 if (sec < year || sec > now) 2616 fstr = time_fmt_old; 2617 2618 /* if a custom time was specified, shouldn't be localized */ 2619 (void) strftime(time_buf, sizeof (time_buf), 2620 (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr, 2621 localtime(&sec)); 2622 } 2623 2624 void 2625 format_attrtime(struct lbuf *p) 2626 { 2627 int tmattr = 0; 2628 int i; 2629 2630 if (p->extm != NULL) { 2631 for (i = 0; i < sacnt; i++) { 2632 if (p->extm[i].name != NULL) { 2633 tmattr = 1; 2634 break; 2635 } 2636 } 2637 } 2638 2639 if (tmattr) { 2640 const char *old_save = time_fmt_old; 2641 const char *new_save = time_fmt_new; 2642 2643 /* Eflg always sets format to FORMAT_ISO_FULL */ 2644 if (!Eflg && !time_custom) { 2645 time_fmt_old = FORMAT_OLD; 2646 time_fmt_new = FORMAT_NEW; 2647 } 2648 2649 format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm); 2650 2651 time_fmt_old = old_save; 2652 time_fmt_new = new_save; 2653 } 2654 } 2655 2656 void 2657 print_time(struct lbuf *p) 2658 { 2659 const char *old_save = time_fmt_old; 2660 const char *new_save = time_fmt_new; 2661 2662 int i = 0; 2663 2664 if (!Eflg) { 2665 time_fmt_old = FORMAT_LONG; 2666 time_fmt_new = FORMAT_LONG; 2667 } 2668 2669 new_line(); 2670 format_time(p->lat.tv_sec, p->lat.tv_nsec); 2671 (void) printf(" timestamp: atime %s\n", time_buf); 2672 format_time(p->lct.tv_sec, p->lct.tv_nsec); 2673 (void) printf(" timestamp: ctime %s\n", time_buf); 2674 format_time(p->lmt.tv_sec, p->lmt.tv_nsec); 2675 (void) printf(" timestamp: mtime %s\n", time_buf); 2676 if (p->extm != NULL) { 2677 while (p->extm[i].nstm != 0 && i < sacnt) { 2678 format_time(p->extm[i].stm, p->extm[i].nstm); 2679 if (p->extm[i].name != NULL) { 2680 (void) printf(" timestamp:" 2681 " %s %s\n", 2682 p->extm[i].name, time_buf); 2683 } 2684 i++; 2685 } 2686 } 2687 2688 time_fmt_old = old_save; 2689 time_fmt_new = new_save; 2690 } 2691 2692 /* 2693 * Check if color definition applies to entry, returns 1 if yes, 0 if no 2694 */ 2695 static int 2696 color_match(const char *fname, mode_t mode, ls_color_t *color) 2697 { 2698 switch (color->ftype) { 2699 case LS_PAT: 2700 { 2701 size_t fname_len, sfx_len; 2702 2703 fname_len = strlen(fname); 2704 sfx_len = strlen(color->sfx); 2705 if (sfx_len > fname_len) 2706 return (0); 2707 2708 if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0) 2709 return (1); 2710 else 2711 return (0); 2712 } 2713 2714 case LS_NORMAL: 2715 return (1); 2716 2717 case LS_FILE: 2718 return (S_ISREG(mode)); 2719 2720 case LS_DIR: 2721 return (S_ISDIR(mode)); 2722 2723 case LS_LINK: 2724 return (S_ISLNK(mode)); 2725 2726 case LS_FIFO: 2727 return (S_ISFIFO(mode)); 2728 2729 case LS_SOCK: 2730 return (S_ISSOCK(mode)); 2731 2732 case LS_DOOR: 2733 return (S_ISDOOR(mode)); 2734 2735 case LS_BLK: 2736 return (S_ISBLK(mode)); 2737 2738 case LS_CHR: 2739 return (S_ISCHR(mode)); 2740 2741 case LS_PORT: 2742 return (S_ISPORT(mode)); 2743 2744 case LS_ORPHAN: 2745 /* this is tested for by gstat */ 2746 return (0); 2747 2748 case LS_SETUID: 2749 return (!S_ISLNK(mode) && (mode & S_ISUID)); 2750 2751 case LS_SETGID: 2752 return (!S_ISLNK(mode) && (mode & S_ISGID)); 2753 2754 case LS_STICKY_OTHER_WRITABLE: 2755 return (!S_ISLNK(mode) && (mode & (S_IWOTH|S_ISVTX))); 2756 2757 case LS_OTHER_WRITABLE: 2758 return (!S_ISLNK(mode) && (mode & S_IWOTH)); 2759 2760 case LS_STICKY: 2761 return (!S_ISLNK(mode) && (mode & S_ISVTX)); 2762 2763 case LS_EXEC: 2764 return (!S_ISLNK(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH))); 2765 } 2766 2767 return (0); 2768 } 2769 2770 static void 2771 dump_color(ls_color_t *c) 2772 { 2773 if (c == NULL) 2774 return; 2775 2776 (void) printf("\n\ttype: "); 2777 switch (c->ftype) { 2778 case LS_NORMAL: 2779 (void) printf("LS_NORMAL"); 2780 break; 2781 case LS_FILE: 2782 (void) printf("LS_FILE"); 2783 break; 2784 case LS_EXEC: 2785 (void) printf("LS_EXEC"); 2786 break; 2787 case LS_DIR: 2788 (void) printf("LS_DIR"); 2789 break; 2790 case LS_LINK: 2791 (void) printf("LS_LINK"); 2792 break; 2793 2794 case LS_FIFO: 2795 (void) printf("LS_FIFO"); 2796 break; 2797 2798 case LS_SOCK: 2799 (void) printf("LS_SOCK"); 2800 break; 2801 2802 case LS_DOOR: 2803 (void) printf("LS_DOOR"); 2804 break; 2805 2806 case LS_BLK: 2807 (void) printf("LS_BLK"); 2808 break; 2809 2810 case LS_CHR: 2811 (void) printf("LS_CHR"); 2812 break; 2813 2814 case LS_PORT: 2815 (void) printf("LS_PORT"); 2816 break; 2817 2818 case LS_STICKY: 2819 (void) printf("LS_STICKY"); 2820 break; 2821 2822 case LS_ORPHAN: 2823 (void) printf("LS_ORPHAN"); 2824 break; 2825 2826 case LS_SETGID: 2827 (void) printf("LS_SETGID"); 2828 break; 2829 2830 case LS_SETUID: 2831 (void) printf("LS_SETUID"); 2832 break; 2833 2834 case LS_OTHER_WRITABLE: 2835 (void) printf("LS_OTHER_WRITABLE"); 2836 break; 2837 2838 case LS_STICKY_OTHER_WRITABLE: 2839 (void) printf("LS_STICKY_OTHER_WRITABLE"); 2840 break; 2841 2842 case LS_PAT: 2843 (void) printf("LS_PAT\n"); 2844 (void) printf("\tpattern: %s", c->sfx); 2845 break; 2846 } 2847 (void) printf("\n"); 2848 (void) printf("\tattr: %d\n", c->attr); 2849 (void) printf("\tfg: %d\n", c->fg); 2850 (void) printf("\tbg: %d\n", c->bg); 2851 (void) printf("\t"); 2852 } 2853 2854 static ls_color_t * 2855 ls_color_find(const char *fname, mode_t mode) 2856 { 2857 int i; 2858 2859 /* 2860 * Colors are sorted from most general lsc_colors[0] to most specific 2861 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with 2862 * most specific color rule and work towards most general. 2863 */ 2864 for (i = lsc_ncolors - 1; i >= 0; --i) 2865 if (color_match(fname, mode, &lsc_colors[i])) 2866 return (&lsc_colors[i]); 2867 2868 return (NULL); 2869 } 2870 2871 static void 2872 ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4, 2873 long int p5, long int p6, long int p7, long int p8, long int p9) 2874 { 2875 char *s; 2876 2877 if (str == NULL) 2878 return; 2879 2880 s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9); 2881 2882 if (s != NULL) 2883 (void) putp(s); 2884 } 2885 2886 static void 2887 ls_start_color(ls_color_t *c) 2888 { 2889 if (c == NULL) 2890 return; 2891 2892 if (lsc_debug) 2893 lsc_match = c; 2894 2895 if (c->attr & LSA_BOLD) 2896 ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2897 if (c->attr & LSA_UNDERSCORE) 2898 ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2899 if (c->attr & LSA_BLINK) 2900 ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2901 if (c->attr & LSA_REVERSE) 2902 ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2903 if (c->attr & LSA_CONCEALED) 2904 ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2905 if (c->attr == LSA_NONE) 2906 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2907 2908 if (c->fg != -1) 2909 ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0); 2910 if (c->bg != -1) 2911 ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0); 2912 } 2913 2914 static void 2915 ls_end_color() 2916 { 2917 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0); 2918 if (lsc_debug) 2919 dump_color(lsc_match); 2920 } 2921 2922 static void 2923 new_color_entry(char *colorstr) 2924 { 2925 static const struct { 2926 const char *s; 2927 ls_cftype_t stype; 2928 } type_map[] = { 2929 { "no", LS_NORMAL }, 2930 { "fi", LS_FILE }, 2931 { "di", LS_DIR }, 2932 { "ln", LS_LINK }, 2933 { "pi", LS_FIFO }, 2934 { "so", LS_SOCK }, 2935 { "do", LS_DOOR }, 2936 { "bd", LS_BLK }, 2937 { "cd", LS_CHR }, 2938 { "or", LS_ORPHAN }, 2939 { "su", LS_SETUID }, 2940 { "sg", LS_SETGID }, 2941 { "tw", LS_STICKY_OTHER_WRITABLE }, 2942 { "ow", LS_OTHER_WRITABLE }, 2943 { "st", LS_STICKY }, 2944 { "ex", LS_EXEC }, 2945 { "po", LS_PORT }, 2946 { NULL, LS_NORMAL } 2947 }; 2948 2949 char *p, *lasts; 2950 int i; 2951 int color, attr; 2952 2953 p = strtok_r(colorstr, "=", &lasts); 2954 if (p == NULL) { 2955 colorflg = 0; 2956 return; 2957 } 2958 2959 if (p[0] == '*') { 2960 lsc_colors[lsc_ncolors].ftype = LS_PAT; 2961 /* don't include the * in the suffix */ 2962 if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) { 2963 colorflg = 0; 2964 return; 2965 } 2966 } else { 2967 lsc_colors[lsc_ncolors].sfx = NULL; 2968 2969 for (i = 0; type_map[i].s != NULL; ++i) { 2970 if (strncmp(type_map[i].s, p, 2) == 0) 2971 break; 2972 } 2973 2974 /* ignore unknown file types */ 2975 if (type_map[i].s == NULL) 2976 return; 2977 2978 lsc_colors[lsc_ncolors].ftype = type_map[i].stype; 2979 } 2980 2981 attr = LSA_NONE; 2982 lsc_colors[lsc_ncolors].fg = -1; 2983 lsc_colors[lsc_ncolors].bg = -1; 2984 for (p = strtok_r(NULL, ";", &lasts); p != NULL; 2985 p = strtok_r(NULL, ";", &lasts)) { 2986 color = strtol(p, NULL, 10); 2987 2988 if (color < 10) { 2989 switch (color) { 2990 case 0: 2991 attr = LSA_NONE; 2992 continue; 2993 case 1: 2994 attr |= LSA_BOLD; 2995 continue; 2996 case 4: 2997 attr |= LSA_UNDERSCORE; 2998 continue; 2999 case 5: 3000 attr |= LSA_BLINK; 3001 continue; 3002 case 7: 3003 attr |= LSA_REVERSE; 3004 continue; 3005 case 8: 3006 attr |= LSA_CONCEALED; 3007 continue; 3008 default: 3009 continue; 3010 } 3011 } 3012 3013 if (color < 40) 3014 lsc_colors[lsc_ncolors].fg = color - 30; 3015 else 3016 lsc_colors[lsc_ncolors].bg = color - 40; 3017 } 3018 3019 lsc_colors[lsc_ncolors].attr = attr; 3020 ++lsc_ncolors; 3021 } 3022 3023 static int 3024 ls_color_compare(const void *p1, const void *p2) 3025 { 3026 const ls_color_t *c1 = (const ls_color_t *)p1; 3027 const ls_color_t *c2 = (const ls_color_t *)p2; 3028 3029 int ret = c1->ftype - c2->ftype; 3030 3031 if (ret != 0) 3032 return (ret); 3033 3034 if (c1->ftype != LS_PAT) 3035 return (ret); 3036 3037 return (strcmp(c1->sfx, c2->sfx)); 3038 } 3039 3040 static void 3041 ls_color_init() 3042 { 3043 static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35" 3044 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01" 3045 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31" 3046 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31" 3047 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31" 3048 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35" 3049 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35" 3050 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35" 3051 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35" 3052 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35" 3053 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35"; 3054 3055 char *colorstr; 3056 char *p, *lasts; 3057 size_t color_sz; 3058 int termret; 3059 int i; 3060 3061 (void) setupterm(NULL, 1, &termret); 3062 if (termret != 1) 3063 return; 3064 3065 if ((colorstr = getenv("LS_COLORS")) == NULL) 3066 colorstr = default_colorstr; 3067 3068 /* 3069 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors 3070 * if there are invalid entries passed in the string (they are ignored) 3071 */ 3072 color_sz = 1; 3073 for (p = strchr(colorstr, ':'); p != NULL && *p != '\0'; 3074 p = strchr(++p, ':')) 3075 ++color_sz; 3076 3077 lsc_colors = calloc(color_sz, sizeof (ls_color_t)); 3078 if (lsc_colors == NULL) { 3079 free(colorstr); 3080 return; 3081 } 3082 3083 for (p = strtok_r(colorstr, ":", &lasts); 3084 p != NULL && lsc_ncolors < color_sz; 3085 p = strtok_r(NULL, ":", &lasts)) 3086 new_color_entry(p); 3087 3088 qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t), 3089 ls_color_compare); 3090 3091 for (i = 0; i < lsc_ncolors; ++i) 3092 if (lsc_colors[i].ftype == LS_ORPHAN) { 3093 lsc_orphan = &lsc_colors[i]; 3094 break; 3095 } 3096 3097 if ((lsc_bold = tigetstr("bold")) == (char *)-1) 3098 lsc_bold = NULL; 3099 3100 if ((lsc_underline = tigetstr("smul")) == (char *)-1) 3101 lsc_underline = NULL; 3102 3103 if ((lsc_blink = tigetstr("blink")) == (char *)-1) 3104 lsc_blink = NULL; 3105 3106 if ((lsc_reverse = tigetstr("rev")) == (char *)-1) 3107 lsc_reverse = NULL; 3108 3109 if ((lsc_concealed = tigetstr("prot")) == (char *)-1) 3110 lsc_concealed = NULL; 3111 3112 if ((lsc_none = tigetstr("sgr0")) == (char *)-1) 3113 lsc_none = NULL; 3114 3115 if ((lsc_setfg = tigetstr("setaf")) == (char *)-1) 3116 lsc_setfg = NULL; 3117 3118 if ((lsc_setbg = tigetstr("setab")) == (char *)-1) 3119 lsc_setbg = NULL; 3120 3121 if (getenv("_LS_COLOR_DEBUG") != NULL) { 3122 int i; 3123 3124 lsc_debug = 1; 3125 for (i = 0; i < lsc_ncolors; ++i) 3126 dump_color(&lsc_colors[i]); 3127 } 3128 } 3129 3130 /* Free extended system attribute lists */ 3131 3132 void 3133 free_sysattr(struct lbuf *p) 3134 { 3135 int i; 3136 3137 if (p->exttr != NULL) { 3138 for (i = 0; i < sacnt; i++) { 3139 if (p->exttr[i].name != NULL) 3140 free(p->exttr[i].name); 3141 } 3142 free(p->exttr); 3143 } 3144 if (p->extm != NULL) { 3145 for (i = 0; i < sacnt; i++) { 3146 if (p->extm[i].name != NULL) 3147 free(p->extm[i].name); 3148 } 3149 free(p->extm); 3150 } 3151 } 3152 3153 /* Allocate extended system attribute list */ 3154 3155 void * 3156 xmalloc(size_t size, struct lbuf *p) 3157 { 3158 if ((p = malloc(size)) == NULL) { 3159 perror("ls"); 3160 free_sysattr(p); 3161 nvlist_free(response); 3162 exit(2); 3163 } 3164 return (p); 3165 } 3166