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