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