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