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