PSARC 2009/228 ls enhancements
authorJason King <jason@ansipunx.net>
Tue, 19 May 2009 19:32:24 -0700
changeset 9664 3ab9bde9a605
parent 9663 ace9a2ac3683
child 9665 03a62442b9f3
PSARC 2009/228 ls enhancements 6803941 Make /usr/bin/ls more compatible with gnu ls 1122699 *ls* ls: would like to have -k option like du does 6838835 getopt_long(3c) in posixly correct mode is too strict with long option arguments 6701026 UNIX03: *vsc* ls -i does not fail for nonexistent target
usr/src/cmd/ls/Makefile.com
usr/src/cmd/ls/ls.c
usr/src/lib/libc/port/gen/getopt_long.c
--- a/usr/src/cmd/ls/Makefile.com	Tue May 19 18:34:13 2009 -0700
+++ b/usr/src/cmd/ls/Makefile.com	Tue May 19 19:32:24 2009 -0700
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # cmd/ls/Makefile.com
 #
 
@@ -35,7 +33,7 @@
 
 include ../../Makefile.cmd
 
-LDLIBS += -lsec -lnvpair -lcmdutils
+LDLIBS += -lsec -lnvpair -lcmdutils -lcurses
 CFLAGS	+=	$(CCVERBOSE)
 $(XPG4) := CFLAGS += -DXPG4
 
--- a/usr/src/cmd/ls/ls.c	Tue May 19 18:34:13 2009 -0700
+++ b/usr/src/cmd/ls/ls.c	Tue May 19 19:32:24 2009 -0700
@@ -18,8 +18,14 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2009 Jason King.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -29,8 +35,6 @@
 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
 /*	  All Rights Reserved	*/
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * List files or directories
  */
@@ -48,6 +52,7 @@
 #include <string.h>
 #include <locale.h>
 #include <curses.h>
+#include <term.h>
 #include <termios.h>
 #include <stdlib.h>
 #include <widec.h>
@@ -64,6 +69,8 @@
 #include <libnvpair.h>
 #include <libcmdutils.h>
 #include <attr.h>
+#include <getopt.h>
+#include <inttypes.h>
 
 #ifndef STANDALONE
 #define	TERMINFO
@@ -107,10 +114,13 @@
  * note that %F and %z are from the ISO C99 standard and are
  * not present in older C libraries
  */
-#define	FORMAT1	 " %b %e  %Y "
-#define	FORMAT2  " %b %e %H:%M "
-#define	FORMAT3  " %b %e %T %Y "
-#define	FORMAT4  " %%F %%T.%.09ld %%z "
+#define	FORMAT_OLD	" %b %e  %Y "
+#define	FORMAT_NEW	" %b %e %H:%M "
+#define	FORMAT_LONG	" %b %e %T %Y "
+#define	FORMAT_ISO_FULL	" %%F %%T.%.09ld %%z "
+#define	FORMAT_ISO_LONG	" %F %R "
+#define	FORMAT_ISO_NEW	" %m-%d %H:%M "
+#define	FORMAT_ISO_OLD	" %F "
 
 #undef BUFSIZ
 #define	BUFSIZ 4096
@@ -166,6 +176,43 @@
 	struct dchain *dc_next;	/* next directory in the chain */
 };
 
+#define	LSA_NONE	(0)
+#define	LSA_BOLD	(1L << 0)
+#define	LSA_UNDERSCORE	(1L << 1)
+#define	LSA_BLINK	(1L << 2)
+#define	LSA_REVERSE	(1L << 3)
+#define	LSA_CONCEALED	(1L << 4)
+
+/* these should be ordered most general to most specific */
+typedef enum LS_CFTYPE {
+	LS_NORMAL,
+	LS_FILE,
+	LS_EXEC,
+	LS_DIR,
+	LS_LINK,
+	LS_FIFO,
+	LS_SOCK,
+	LS_DOOR,
+	LS_BLK,
+	LS_CHR,
+	LS_PORT,
+	LS_STICKY,
+	LS_ORPHAN,
+	LS_SETGID,
+	LS_SETUID,
+	LS_OTHER_WRITABLE,
+	LS_STICKY_OTHER_WRITABLE,
+	LS_PAT
+} ls_cftype_t;
+
+typedef struct ls_color {
+	char		*sfx;
+	ls_cftype_t	ftype;
+	int		attr;
+	int		fg;
+	int		bg;
+} ls_color_t;
+
 /*
  * A numbuf_t is used when converting a number to a string representation
  */
@@ -207,6 +254,9 @@
 			    long scale);
 static void		record_ancestry(char *, struct stat *, struct lbuf *,
 			    int, struct ditem *);
+static void		ls_color_init(void);
+static void		ls_start_color(struct lbuf *);
+static void		ls_end_color(void);
 
 static int		aflg;
 static int		atflg;
@@ -228,8 +278,11 @@
 static int		sflg;
 static int		tflg;
 static int		uflg;
+static int		Uflg;
+static int		wflg;
 static int		xflg;
 static int		Aflg;
+static int		Bflg;
 static int		Cflg;
 static int		Eflg;
 static int		Fflg;
@@ -252,15 +305,18 @@
 static long		hscale;
 static mode_t		flags;
 static int		err = 0;	/* Contains return code */
+static int		colorflg;
+static int		file_typeflg;
 
 static uid_t		lastuid	= (uid_t)-1;
 static gid_t		lastgid = (gid_t)-1;
 static char		*lastuname = NULL;
 static char		*lastgname = NULL;
 
-/* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg are on */
+/* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
 static int		statreq;
 
+static uint64_t		block_size = 1;
 static char		*dotp = ".";
 
 static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
@@ -275,8 +331,25 @@
 
 static struct	winsize	win;
 
+/* if time_fmt_new is left NULL, time_fmt_old is used for all times */
+static const char	*time_fmt_old = FORMAT_OLD;	/* non-recent files */
+static const char	*time_fmt_new = FORMAT_NEW;	/* recent files */
+static int		time_custom;	/* != 0 if a custom format */
 static char	time_buf[FMTSIZE];	/* array to hold day and time */
 
+static int		lsc_debug;
+static ls_color_t	*lsc_match;
+static ls_color_t	*lsc_colors;
+static size_t		lsc_ncolors;
+static char		*lsc_bold;
+static char		*lsc_underline;
+static char		*lsc_blink;
+static char		*lsc_reverse;
+static char		*lsc_concealed;
+static char		*lsc_none;
+static char		*lsc_setfg;
+static char		*lsc_setbg;
+
 #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
 				    (strcmp((d) + (l) - 2, "/.") != 0))
 
@@ -286,8 +359,7 @@
 static int get_sysxattr(char *, struct lbuf *);
 static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
 static void set_sysattrtm_display(char *, struct lbuf *);
-static void format_time(const char *, time_t);
-static void format_etime(const char *, time_t, time_t);
+static void format_time(time_t, time_t);
 static void print_time(struct lbuf *);
 static void format_attrtime(struct lbuf *);
 static void *xmalloc(size_t, struct lbuf *);
@@ -296,6 +368,36 @@
 static nvlist_t	*response;
 static int acl_err;
 
+const struct option long_options[] = {
+	{ "all", no_argument, NULL, 'a' },
+	{ "almost-all", no_argument, NULL, 'A' },
+	{ "escape", no_argument, NULL, 'b' },
+	{ "classify", no_argument, NULL, 'F' },
+	{ "human-readable", no_argument, NULL, 'h' },
+	{ "dereference", no_argument, NULL, 'L' },
+	{ "dereference-command-line", no_argument, NULL, 'H' },
+	{ "ignore-backups", no_argument, NULL, 'B' },
+	{ "inode", no_argument, NULL, 'i' },
+	{ "numeric-uid-gid", no_argument, NULL, 'n' },
+	{ "no-group", no_argument, NULL, 'o' },
+	{ "hide-control-chars", no_argument, NULL, 'q' },
+	{ "reverse", no_argument, NULL, 'r' },
+	{ "recursive", no_argument, NULL, 'R' },
+	{ "size", no_argument, NULL, 's' },
+	{ "width", required_argument, NULL, 'w' },
+
+	/* no short options for these */
+	{ "block-size", required_argument, NULL, 0 },
+	{ "full-time", no_argument, NULL, 0 },
+	{ "si", no_argument, NULL, 0 },
+	{ "color", optional_argument, NULL, 0 },
+	{ "colour", optional_argument, NULL, 0},
+	{ "file-type", no_argument, NULL, 0 },
+	{ "time-style", required_argument, NULL, 0 },
+
+	{0, 0, 0, 0}
+};
+
 int
 main(int argc, char *argv[])
 {
@@ -304,6 +406,7 @@
 	int		width;
 	int		amino = 0;
 	int		opterr = 0;
+	int		option_index = 0;
 	struct lbuf	*ep;
 	struct lbuf	lb;
 	struct ditem	*myinfo;
@@ -327,9 +430,259 @@
 		mflg = 0;
 	}
 
-	while ((c = getopt(argc, argv,
-	    "aAbcCdeEfFghHilLmnopqrRsStux1@vV/:%:")) != EOF)
+	while ((c = getopt_long(argc, argv,
+	    "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
+	    &option_index)) != -1)
 		switch (c) {
+		case 0:
+			/* non-short options */
+			if (strcmp(long_options[option_index].name,
+			    "color") == 0 ||
+			    strcmp(long_options[option_index].name,
+			    "colour") == 0) {
+				if (optarg == NULL ||
+				    strcmp(optarg, "always") == 0 ||
+				    strcmp(optarg, "yes") == 0 ||
+				    strcmp(optarg, "force") == 0) {
+					colorflg++;
+					statreq++;
+					continue;
+				}
+
+				if ((strcmp(optarg, "auto") == 0 ||
+				    strcmp(optarg, "tty") == 0 ||
+				    strcmp(optarg, "if-tty") == 0) &&
+				    isatty(1) == 1) {
+					colorflg++;
+					statreq++;
+					continue;
+				}
+
+				if (strcmp(optarg, "never") == 0 ||
+				    strcmp(optarg, "no") == 0 ||
+				    strcmp(optarg, "none") == 0) {
+					colorflg = 0;
+					continue;
+				}
+				(void) fprintf(stderr,
+				    gettext("Invalid argument '%s' for "
+				    "--color\n"), optarg);
+				++opterr;
+				continue;
+			}
+
+			if (strcmp(long_options[option_index].name,
+			    "si") == 0) {
+				hflg++;
+				hscale = 1000;
+				continue;
+			}
+
+			if (strcmp(long_options[option_index].name,
+			    "block-size") == 0) {
+				size_t scale_len = strlen(optarg);
+				uint64_t scale = 1;
+				uint64_t kilo = 1024;
+				char scale_c;
+
+				if (scale_len == 0) {
+					(void) fprintf(stderr, gettext(
+					    "Invalid block size \'%s\'\n"),
+					    optarg);
+					exit(1);
+				}
+
+				scale_c = optarg[scale_len - 1];
+				if (scale_c == 'B') {
+					/* need at least digit, scale, B */
+					if (scale_len < 3) {
+						(void) fprintf(stderr, gettext(
+						    "Invalid block size "
+						    "\'%s\'\n"), optarg);
+						exit(1);
+					}
+					kilo = 1000;
+					scale_c = optarg[scale_len - 2];
+					if (isdigit(scale_c)) {
+						(void) fprintf(stderr,
+						    gettext("Invalid block size"
+						    " \'%s\'\n"), optarg);
+						exit(1);
+					}
+					/*
+					 * make optarg[scale_len - 1] point to
+					 * the scale factor
+					 */
+					--scale_len;
+				}
+
+				switch (scale_c) {
+				case 'y':
+				case 'Y':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'Z':
+				case 'z':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'E':
+				case 'e':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'P':
+				case 'p':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'T':
+				case 't':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'G':
+				case 'g':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'M':
+				case 'm':
+					scale *= kilo;
+					/*FALLTHROUGH*/
+				case 'K':
+				case 'k':
+					scale *= kilo;
+					break;
+				default:
+					if (!isdigit(scale_c)) {
+						(void) fprintf(stderr,
+						    gettext("Invalid character "
+						    "following block size in "
+						    "\'%s\'\n"), optarg);
+						exit(1);
+					}
+				}
+
+				/* NULL out scale constant if present */
+				if (scale > 1 && !isdigit(scale_c))
+					optarg[scale_len - 1] = '\0';
+
+				/* Based on testing, this is what GNU ls does */
+				block_size = strtoll(optarg, NULL, 0) * scale;
+				if (block_size < 1) {
+					(void) fprintf(stderr,
+					    gettext("Invalid block size "
+					    "\'%s\'\n"), optarg);
+					exit(1);
+				}
+				continue;
+			}
+
+			if (strcmp(long_options[option_index].name,
+			    "file-type") == 0) {
+				file_typeflg++;
+				Fflg++;
+				statreq++;
+				continue;
+			}
+
+
+			if (strcmp(long_options[option_index].name,
+			    "full-time") == 0) {
+				Eflg++;
+				statreq++;
+				eflg = 0;
+				time_fmt_old = FORMAT_ISO_FULL;
+				time_fmt_new = FORMAT_ISO_FULL;
+				continue;
+			}
+
+			if (strcmp(long_options[option_index].name,
+			    "time-style") == 0) {
+				/* like -E, but doesn't imply -l */
+				if (strcmp(optarg, "full-iso") == 0) {
+					Eflg++;
+					statreq++;
+					eflg = 0;
+					time_fmt_old = FORMAT_ISO_FULL;
+					time_fmt_new = FORMAT_ISO_FULL;
+					continue;
+				}
+				if (strcmp(optarg, "long-iso") == 0) {
+					statreq++;
+					Eflg = 0;
+					eflg = 0;
+					time_fmt_old = FORMAT_ISO_LONG;
+					time_fmt_new = FORMAT_ISO_LONG;
+					continue;
+				}
+				if (strcmp(optarg, "iso") == 0) {
+					statreq++;
+					Eflg = 0;
+					eflg = 0;
+					time_fmt_old = FORMAT_ISO_OLD;
+					time_fmt_new = FORMAT_ISO_NEW;
+					continue;
+				}
+				/* should be the default */
+				if (strcmp(optarg, "locale") == 0) {
+					time_fmt_old = FORMAT_OLD;
+					time_fmt_new = FORMAT_NEW;
+					continue;
+				}
+				if (optarg[0] == '+') {
+					char	*told, *tnew;
+					char	*p;
+					size_t	timelen = strlen(optarg);
+
+					p = strchr(optarg, '\n');
+					if (p != NULL)
+						*p++ = '\0';
+
+					/*
+					 * Time format requires a leading and
+					 * trailing space
+					 * Add room for 3 spaces + 2 nulls
+					 * The + in optarg is replaced with
+					 * a space.
+					 */
+					timelen += 2 + 3;
+					told = malloc(timelen);
+					if (told == NULL) {
+						perror("Out of memory");
+						exit(1);
+					}
+
+					(void) memset(told, 0, timelen);
+					told[0] = ' ';
+					(void) strlcat(told, &optarg[1],
+					    timelen);
+					(void) strlcat(told, " ", timelen);
+
+					if (p != NULL) {
+						size_t tnew_len;
+
+						tnew = told + strlen(told) + 1;
+						tnew_len = timelen -
+						    strlen(told) - 1;
+
+						tnew[0] = ' ';
+						(void) strlcat(tnew, p,
+						    tnew_len);
+						(void) strlcat(tnew, " ",
+						    tnew_len);
+						time_fmt_new =
+						    (const char *)tnew;
+					} else {
+						time_fmt_new =
+						    (const char *)told;
+					}
+
+					time_fmt_old = (const char *)told;
+					time_custom = 1;
+					continue;
+				}
+				continue;
+			}
+
+			continue;
+
 		case 'a':
 			aflg++;
 			continue;
@@ -340,6 +693,9 @@
 			bflg = 1;
 			qflg = 0;
 			continue;
+		case 'B':
+			Bflg = 1;
+			continue;
 		case 'c':
 			uflg = 0;
 			atm = 0;
@@ -363,12 +719,16 @@
 			lflg++;
 			statreq++;
 			Eflg = 0;
+			time_fmt_old = FORMAT_LONG;
+			time_fmt_new = FORMAT_LONG;
 			continue;
 		case 'E':
 			Eflg++;
 			lflg++;
 			statreq++;
 			eflg = 0;
+			time_fmt_old = FORMAT_ISO_FULL;
+			time_fmt_new = FORMAT_ISO_FULL;
 			continue;
 		case 'f':
 			fflg++;
@@ -394,6 +754,9 @@
 		case 'i':
 			iflg++;
 			continue;
+		case 'k':
+			block_size = 1024;
+			continue;
 		case 'l':
 			lflg++;
 			statreq++;
@@ -449,14 +812,21 @@
 			continue;
 		case 'S':
 			tflg = 0;
+			Uflg = 0;
 			Sflg++;
 			statreq++;
 			continue;
 		case 't':
 			Sflg = 0;
+			Uflg = 0;
 			tflg++;
 			statreq++;
 			continue;
+		case 'U':
+			Sflg = 0;
+			tflg = 0;
+			Uflg++;
+			continue;
 		case 'u':
 			cflg = 0;
 			atm = 0;
@@ -480,6 +850,10 @@
 			xflg = 0;
 			mflg = 0;
 			continue;
+		case 'w':
+			wflg++;
+			num_cols = atoi(optarg);
+			continue;
 		case 'x':
 			xflg = 1;
 			Cflg = 1;
@@ -573,9 +947,10 @@
 			opterr++;
 			continue;
 		}
+
 	if (opterr) {
 		(void) fprintf(stderr, gettext(
-		    "usage: ls -aAbcCdeEfFghHilLmnopqrRsStuxvV1@/%[c | v]"
+		    "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
 		    "%%[atime | crtime | ctime | mtime | all]"
 		    " [files]\n"));
 		exit(2);
@@ -607,7 +982,7 @@
 		Cflg = mflg = 0;
 	}
 
-	if (Cflg || mflg) {
+	if (!wflg && (Cflg || mflg)) {
 		char *clptr;
 		if ((clptr = getenv("COLUMNS")) != NULL)
 			num_cols = atoi(clptr);
@@ -617,11 +992,12 @@
 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
 		}
 #endif
-		if (num_cols < 20 || num_cols > 1000)
-			/* assume it is an error */
-			num_cols = 80;
 	}
 
+	if (num_cols < 20 || num_cols > 1000)
+		/* assume it is an error */
+		num_cols = 80;
+
 	/* allocate space for flist and the associated	*/
 	/* data structures (lbufs)			*/
 	maxfils = quantn;
@@ -698,12 +1074,17 @@
 			err = 2;
 	}
 	colwidth = fixedwidth + filewidth;
-	qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
-	    (int (*)(const void *, const void *))compar);
+	if (!Uflg)
+		qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
+		    (int (*)(const void *, const void *))compar);
 	for (i = 0; i < nargs; i++) {
 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
 			break;
 	}
+
+	if (colorflg)
+		ls_color_init();
+
 	pem(&flist[0], &flist[i], 0);
 	for (; i < nargs; i++) {
 		pdirectory(flist[i]->ln.namep, Rflg ||
@@ -731,6 +1112,7 @@
 			free(dtemp);
 		}
 	}
+
 	return (err);
 }
 
@@ -772,7 +1154,7 @@
 	rddir(name, myinfo);
 	if (nomocore)
 		return;
-	if (fflg == 0)
+	if (fflg == 0 && Uflg == 0)
 		qsort(&flist[lp], (unsigned)(nfiles - lp),
 		    sizeof (struct lbuf *),
 		    (int (*)(const void *, const void *))compar);
@@ -906,22 +1288,18 @@
 			curcol += printf("%7s",
 			    number_to_scaled_string(hbuf, p->lsize, hscale));
 		} else {
-			curcol += printf((p->lsize < (off_t)10000000) ?
-			    "%7lld" : "%lld", p->lsize);
+			uint64_t bsize = p->lsize / block_size;
+
+			/*
+			 * Round up only when using blocks > 1 byte, otherwise
+			 * 'normal' sizes display 1 byte too large.
+			 */
+			if (p->lsize % block_size != 0)
+				bsize++;
+
+			curcol += printf("%7" PRIu64, bsize);
 		}
-		if (eflg)
-			format_time(FORMAT3, p->lmtime.tv_sec);
-		else if (Eflg)
-			/* fill in nanoseconds first */
-			format_etime(FORMAT4, p->lmtime.tv_sec,
-			    p->lmtime.tv_nsec);
-		else {
-			if ((p->lmtime.tv_sec < year) ||
-			    (p->lmtime.tv_sec > now))
-				format_time(FORMAT1, p->lmtime.tv_sec);
-			else
-				format_time(FORMAT2, p->lmtime.tv_sec);
-		}
+		format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
 		/* format extended system attribute time */
 		if (tmflg && crtm)
 			format_attrtime(p);
@@ -948,7 +1326,8 @@
 			dmark = "@";
 		else if (p->ltype == 's')
 			dmark = "=";
-		else if (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH))
+		else if (!file_typeflg &&
+		    (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
 			dmark = "*";
 		else
 			dmark = "";
@@ -959,6 +1338,10 @@
 		(void) strcpy(buf + 4, p->flinkto);
 		dmark = buf;
 	}
+
+	if (colorflg)
+		ls_start_color(p);
+
 	if (p->lflags & ISARG) {
 		if (qflg || bflg)
 			pprintf(p->ln.namep, dmark);
@@ -977,6 +1360,9 @@
 		}
 	}
 
+	if (colorflg)
+		ls_end_color();
+
 	/* Display extended system attributes */
 	if (saflg) {
 		int i;
@@ -1149,6 +1535,10 @@
 				 */
 				continue;
 
+			/* skip entries ending in ~ if -B was given */
+			if (Bflg &&
+			    dentry->d_name[strlen(dentry->d_name) - 1] == '~')
+				continue;
 			if (Cflg || mflg) {
 				width = strcol((unsigned char *)dentry->d_name);
 				if (width > filewidth)
@@ -1404,6 +1794,7 @@
 				 * Print error message in case of dangling link.
 				 */
 				perror(file);
+				err = 2;
 			}
 			nfiles--;
 			return (NULL);
@@ -1418,6 +1809,7 @@
 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
 			if (lstat(file, &statb) < 0) {
 				perror(file);
+				err = 2;
 			}
 		}
 
@@ -2175,31 +2567,31 @@
 }
 
 void
-format_time(const char *format, time_t sec)
+format_time(time_t sec, time_t nsec)
 {
+	const char *fstr = time_fmt_new;
+	char fmt_buf[FMTSIZE];
 
+	if (Eflg) {
+		(void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
+		(void) strftime(time_buf, sizeof (time_buf), fmt_buf,
+		    localtime(&sec));
+		return;
+	}
+
+	if (sec < year || sec > now)
+		fstr = time_fmt_old;
+
+	/* if a custom time was specified, shouldn't be localized */
 	(void) strftime(time_buf, sizeof (time_buf),
-	    dcgettext(NULL, format, LC_TIME),
+	    (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
 	    localtime(&sec));
 }
 
 void
-format_etime(const char *format, time_t sec, time_t nsec)
-{
-	char fmt_buf[FMTSIZE];
-
-	(void) snprintf(fmt_buf, FMTSIZE,
-	    format, nsec);
-	(void) strftime(time_buf, sizeof (time_buf),
-	    fmt_buf, localtime(&sec));
-}
-
-/* Format timestamp extended system attributes */
-
-void
 format_attrtime(struct lbuf *p)
 {
-	int	tmattr = 0;
+	int tmattr = 0;
 	int i;
 
 	if (p->extm != NULL) {
@@ -2210,71 +2602,511 @@
 			}
 		}
 	}
+
 	if (tmattr) {
-		if (Eflg)
-			format_etime(FORMAT4, (time_t)p->extm[i].stm,
-			    (time_t)p->extm[i].nstm);
-		else  {
-			if ((p->lmtime.tv_sec < year) ||
-			    (p->lmtime.tv_sec > now))
-				format_time(FORMAT1,
-				    (time_t)p->extm[i].stm);
-			else
-				format_time(FORMAT2,
-				    (time_t)p->extm[i].stm);
+		const char *old_save = time_fmt_old;
+		const char *new_save = time_fmt_new;
+
+		/* Eflg always sets format to FORMAT_ISO_FULL */
+		if (!Eflg && !time_custom) {
+			time_fmt_old = FORMAT_OLD;
+			time_fmt_new = FORMAT_NEW;
 		}
+
+		format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
+
+		time_fmt_old = old_save;
+		time_fmt_new = new_save;
 	}
 }
 
 void
 print_time(struct lbuf *p)
 {
+	const char *old_save = time_fmt_old;
+	const char *new_save = time_fmt_new;
+
 	int i = 0;
 
+	if (!Eflg) {
+		time_fmt_old = FORMAT_LONG;
+		time_fmt_new = FORMAT_LONG;
+	}
+
 	new_line();
-	if (Eflg) {
-		format_etime(FORMAT4, p->lat.tv_sec, p->lat.tv_nsec);
-		(void) printf("		timestamp: atime	%s\n",
-		    time_buf);
-		format_etime(FORMAT4, p->lct.tv_sec, p->lct.tv_nsec);
-		(void) printf("		timestamp: ctime	%s\n",
-		    time_buf);
-		format_etime(FORMAT4, p->lmt.tv_sec, p->lmt.tv_nsec);
-		(void) printf("		timestamp: mtime	%s\n",
-		    time_buf);
-		if (p->extm != NULL) {
-			while (p->extm[i].nstm != 0 && i < sacnt) {
-				format_etime(FORMAT4, p->extm[i].stm,
-				    p->extm[i].nstm);
-				if (p->extm[i].name != NULL) {
-					(void) printf("		timestamp:"
-					    " %s	%s\n",
-					    p->extm[i].name, time_buf);
-				}
-				i++;
+	format_time(p->lat.tv_sec, p->lat.tv_nsec);
+	(void) printf("         timestamp: atime        %s\n", time_buf);
+	format_time(p->lct.tv_sec, p->lct.tv_nsec);
+	(void) printf("         timestamp: ctime        %s\n", time_buf);
+	format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
+	(void) printf("         timestamp: mtime        %s\n", time_buf);
+	if (p->extm != NULL) {
+		while (p->extm[i].nstm != 0 && i < sacnt) {
+			format_time(p->extm[i].stm, p->extm[i].nstm);
+			if (p->extm[i].name != NULL) {
+				(void) printf("         timestamp:"
+				    " %s        %s\n",
+				    p->extm[i].name, time_buf);
+			}
+			i++;
+		}
+	}
+
+	time_fmt_old = old_save;
+	time_fmt_new = new_save;
+}
+
+/*
+ * Check if color definition applies to entry, returns 1 if yes, 0 if no
+ */
+static int
+color_match(struct lbuf *entry, ls_color_t *color)
+{
+	switch (color->ftype) {
+	case LS_PAT:
+	{
+		char	*fname;
+		size_t	fname_len, sfx_len;
+
+		if (entry->lflags & ISARG)
+			fname = entry->ln.namep;
+		else
+			fname = entry->ln.lname;
+
+		fname_len = strlen(fname);
+		sfx_len = strlen(color->sfx);
+		if (sfx_len > fname_len)
+			return (0);
+
+		if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
+			return (1);
+		else
+			return (0);
+	}
+
+	case LS_NORMAL:
+		return (1);
+
+	case LS_FILE:
+		return ((entry->ltype == '-'));
+
+	case LS_DIR:
+		return ((entry->ltype == 'd'));
+
+	case LS_LINK:
+		return ((entry->ltype == 'l'));
+
+	case LS_FIFO:
+		return ((entry->ltype == 'p'));
+
+	case LS_SOCK:
+		return ((entry->ltype == 's'));
+
+	case LS_DOOR:
+		return ((entry->ltype == 'D'));
+
+	case LS_BLK:
+		return ((entry->ltype == 'b'));
+
+	case LS_CHR:
+		return ((entry->ltype == 'c'));
+
+	case LS_PORT:
+		return ((entry->ltype == 'P'));
+
+	case LS_ORPHAN:
+	{
+		struct stat st;
+		int rc;
+
+		if (entry->ltype != 'l')
+			return (0);
+		if (entry->flinkto == NULL)
+			return (1);
+
+		if (entry->lflags & ISARG)
+			rc = stat(entry->ln.namep, &st);
+		else
+			rc = stat(entry->ln.lname, &st);
+
+		if (rc == -1 && errno == ENOENT)
+			return (1);
+
+		return (0);
+	}
+
+	case LS_SETUID:
+		return (entry->ltype != 'l' && (entry->lflags & (S_ISUID)));
+
+	case LS_SETGID:
+		return (entry->ltype != 'l' && (entry->lflags & (S_ISGID)));
+
+	case LS_STICKY_OTHER_WRITABLE:
+		return (entry->ltype != 'l' &&
+		    (entry->lflags & (S_IWOTH|S_ISVTX)));
+
+	case LS_OTHER_WRITABLE:
+		return (entry->ltype != 'l' && (entry->lflags & (S_IWOTH)));
+
+	case LS_STICKY:
+		return (entry->ltype != 'l' && (entry->lflags & (S_ISVTX)));
+
+	case LS_EXEC:
+		return (entry->ltype != 'l' &&
+		    (entry->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)));
+	}
+
+	return (0);
+}
+
+static void
+dump_color(ls_color_t *c)
+{
+	if (c == NULL)
+		return;
+
+	(void) printf("\n\ttype: ");
+	switch (c->ftype) {
+	case LS_NORMAL:
+		(void) printf("LS_NORMAL");
+		break;
+	case LS_FILE:
+		(void) printf("LS_FILE");
+		break;
+	case LS_EXEC:
+		(void) printf("LS_EXEC");
+		break;
+	case LS_DIR:
+		(void) printf("LS_DIR");
+		break;
+	case LS_LINK:
+		(void) printf("LS_LINK");
+		break;
+
+	case LS_FIFO:
+		(void) printf("LS_FIFO");
+		break;
+
+	case LS_SOCK:
+		(void) printf("LS_SOCK");
+		break;
+
+	case LS_DOOR:
+		(void) printf("LS_DOOR");
+		break;
+
+	case LS_BLK:
+		(void) printf("LS_BLK");
+		break;
+
+	case LS_CHR:
+		(void) printf("LS_CHR");
+		break;
+
+	case LS_PORT:
+		(void) printf("LS_PORT");
+		break;
+
+	case LS_STICKY:
+		(void) printf("LS_STICKY");
+		break;
+
+	case LS_ORPHAN:
+		(void) printf("LS_ORPHAN");
+		break;
+
+	case LS_SETGID:
+		(void) printf("LS_SETGID");
+		break;
+
+	case LS_SETUID:
+		(void) printf("LS_SETUID");
+		break;
+
+	case LS_OTHER_WRITABLE:
+		(void) printf("LS_OTHER_WRITABLE");
+		break;
+
+	case LS_STICKY_OTHER_WRITABLE:
+		(void) printf("LS_STICKY_OTHER_WRITABLE");
+		break;
+
+	case LS_PAT:
+		(void) printf("LS_PAT\n");
+		(void) printf("\tpattern: %s", c->sfx);
+		break;
+	}
+	(void) printf("\n");
+	(void) printf("\tattr: %d\n", c->attr);
+	(void) printf("\tfg: %d\n", c->fg);
+	(void) printf("\tbg: %d\n", c->bg);
+	(void) printf("\t");
+}
+
+static ls_color_t *
+get_color_attr(struct lbuf *l)
+{
+	int i;
+
+	/*
+	 * Colors are sorted from most general lsc_colors[0] to most specific
+	 * lsc_colors[lsc_ncolors - 1] by ls_color_init().  Start search with
+	 * most specific color rule and work towards most general.
+	 */
+	for (i = lsc_ncolors - 1; i >= 0; --i)
+		if (color_match(l, &lsc_colors[i]))
+			return (&lsc_colors[i]);
+
+	return (NULL);
+}
+
+static void
+ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
+    long int p5, long int p6, long int p7, long int p8, long int p9)
+{
+	char *s;
+
+	if (str == NULL)
+		return;
+
+	s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+
+	if (s != NULL)
+		(void) putp(s);
+}
+
+static void
+ls_start_color(struct lbuf *l)
+{
+	ls_color_t *c = get_color_attr(l);
+
+	if (c == NULL)
+		return;
+
+	if (lsc_debug)
+		lsc_match = c;
+
+	if (c->attr & LSA_BOLD)
+		ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (c->attr & LSA_UNDERSCORE)
+		ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (c->attr & LSA_BLINK)
+		ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (c->attr & LSA_REVERSE)
+		ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (c->attr & LSA_CONCEALED)
+		ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (c->attr == LSA_NONE)
+		ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+	if (c->fg != -1)
+		ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (c->bg != -1)
+		ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+static void
+ls_end_color()
+{
+	ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+	if (lsc_debug)
+		dump_color(lsc_match);
+}
+
+static void
+new_color_entry(char *colorstr)
+{
+	static const struct {
+		const char	*s;
+		ls_cftype_t	stype;
+	} type_map[] = {
+		{ "no", LS_NORMAL },
+		{ "fi", LS_FILE },
+		{ "di", LS_DIR },
+		{ "ln", LS_LINK },
+		{ "pi", LS_FIFO },
+		{ "so", LS_SOCK },
+		{ "do", LS_DOOR },
+		{ "bd", LS_BLK },
+		{ "cd", LS_CHR },
+		{ "or", LS_ORPHAN },
+		{ "su", LS_SETUID },
+		{ "sg", LS_SETGID },
+		{ "tw", LS_STICKY_OTHER_WRITABLE },
+		{ "ow", LS_OTHER_WRITABLE },
+		{ "st", LS_STICKY },
+		{ "ex", LS_EXEC },
+		{ "po", LS_PORT },
+		{ NULL, LS_NORMAL }
+	};
+
+	char		*p, *lasts;
+	int		i;
+	int		color, attr;
+
+	p = strtok_r(colorstr, "=", &lasts);
+	if (p == NULL) {
+		colorflg = 0;
+		return;
+	}
+
+	if (p[0] == '*') {
+		lsc_colors[lsc_ncolors].ftype = LS_PAT;
+		/* don't include the * in the suffix */
+		if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
+			colorflg = 0;
+			return;
+		}
+	} else {
+		lsc_colors[lsc_ncolors].sfx = NULL;
+
+		for (i = 0; type_map[i].s != NULL; ++i) {
+			if (strncmp(type_map[i].s, p, 2) == 0)
+				break;
+		}
+
+		/* ignore unknown file types */
+		if (type_map[i].s == NULL)
+			return;
+
+		lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
+	}
+
+	attr = LSA_NONE;
+	lsc_colors[lsc_ncolors].fg = -1;
+	lsc_colors[lsc_ncolors].bg = -1;
+	for (p = strtok_r(NULL, ";", &lasts); p != NULL;
+	    p = strtok_r(NULL, ";", &lasts)) {
+		color = strtol(p, NULL, 10);
+
+		if (color < 10) {
+			switch (color) {
+			case 0:
+				attr = LSA_NONE;
+				continue;
+			case 1:
+				attr |= LSA_BOLD;
+				continue;
+			case 4:
+				attr |= LSA_UNDERSCORE;
+				continue;
+			case 5:
+				attr |= LSA_BLINK;
+				continue;
+			case 7:
+				attr |= LSA_REVERSE;
+				continue;
+			case 8:
+				attr |= LSA_CONCEALED;
+				continue;
+			default:
+				continue;
 			}
 		}
-	} else {
-		format_time(FORMAT3, p->lat.tv_sec);
-		(void) printf("		timestamp: atime	%s\n",
-		    time_buf);
-		format_time(FORMAT3, p->lct.tv_sec);
-		(void) printf("		timestamp: ctime	%s\n",
-		    time_buf);
-		format_time(FORMAT3, p->lmt.tv_sec);
-		(void) printf("		timestamp: mtime	%s\n",
-		    time_buf);
-		if (p->extm != NULL) {
-			while (p->extm[i].stm != 0 && i < sacnt) {
-				format_time(FORMAT3, p->extm[i].stm);
-				if (p->extm[i].name != NULL) {
-					(void) printf("		timestamp:"
-					    " %s	%s\n",
-					    p->extm[i].name, time_buf);
-				}
-				i++;
-			}
-		}
+
+		if (color < 40)
+			lsc_colors[lsc_ncolors].fg = color - 30;
+		else
+			lsc_colors[lsc_ncolors].bg = color - 40;
+	}
+
+	lsc_colors[lsc_ncolors].attr = attr;
+	++lsc_ncolors;
+}
+
+static int
+ls_color_compare(const void *p1, const void *p2)
+{
+	const ls_color_t *c1 = (const ls_color_t *)p1;
+	const ls_color_t *c2 = (const ls_color_t *)p2;
+
+	int ret = c1->ftype - c2->ftype;
+
+	if (ret != 0)
+		return (ret);
+
+	if (c1->ftype != LS_PAT)
+		return (ret);
+
+	return (strcmp(c1->sfx, c2->sfx));
+}
+
+static void
+ls_color_init()
+{
+	static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
+	    ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
+	    ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
+	    ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
+	    ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
+	    ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
+	    ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
+	    ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
+	    ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
+	    ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
+	    ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
+
+	char    *colorstr;
+	char    *p, *lasts;
+	size_t  color_sz;
+	int	termret;
+
+	(void) setupterm(NULL, 1, &termret);
+	if (termret != 1)
+		return;
+
+	if ((colorstr = getenv("LS_COLORS")) == NULL)
+		colorstr = default_colorstr;
+
+	color_sz = 0;
+	for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
+	    p = strchr(++p, ':'))
+		++color_sz;
+
+	lsc_colors = calloc(color_sz, sizeof (ls_color_t));
+	if (lsc_colors == NULL) {
+		free(colorstr);
+		return;
+	}
+
+	for (p = strtok_r(colorstr, ":", &lasts);
+	    p != NULL && lsc_ncolors < color_sz;
+	    p = strtok_r(NULL, ":", &lasts))
+		new_color_entry(p);
+
+	qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
+	    ls_color_compare);
+
+	if ((lsc_bold = tigetstr("bold")) == (char *)-1)
+		lsc_bold = NULL;
+
+	if ((lsc_underline = tigetstr("smul")) == (char *)-1)
+		lsc_underline = NULL;
+
+	if ((lsc_blink = tigetstr("blink")) == (char *)-1)
+		lsc_blink = NULL;
+
+	if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
+		lsc_reverse = NULL;
+
+	if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
+		lsc_concealed = NULL;
+
+	if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
+		lsc_none = NULL;
+
+	if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
+		lsc_setfg = NULL;
+
+	if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
+		lsc_setbg = NULL;
+
+	if (getenv("_LS_COLOR_DEBUG") != NULL) {
+		int i;
+
+		lsc_debug = 1;
+		for (i = 0; i < lsc_ncolors; ++i)
+			dump_color(&lsc_colors[i]);
 	}
 }
 
--- a/usr/src/lib/libc/port/gen/getopt_long.c	Tue May 19 18:34:13 2009 -0700
+++ b/usr/src/lib/libc/port/gen/getopt_long.c	Tue May 19 19:32:24 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -65,8 +65,6 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #pragma weak _getopt_clip = getopt_clip
 #pragma weak _getopt_long = getopt_long
 #pragma weak _getopt_long_only = getopt_long_only
@@ -495,7 +493,6 @@
 	if (posixly_correct) {
 		flags &= ~FLAG_PERMUTE;
 		flags &= ~FLAG_ALLARGS;
-		flags &= ~FLAG_OPTIONAL_ARGS;
 	}
 
 	/*