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