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