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