# HG changeset patch # User Jason King # Date 1242786744 25200 # Node ID 3ab9bde9a60583a461630075009d5d75aaddaeab # Parent ace9a2ac36839dbba78b6d796dfcb012d1fb740b 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 diff -r ace9a2ac3683 -r 3ab9bde9a605 usr/src/cmd/ls/Makefile.com --- 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 diff -r ace9a2ac3683 -r 3ab9bde9a605 usr/src/cmd/ls/ls.c --- 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 #include #include +#include #include #include #include @@ -64,6 +69,8 @@ #include #include #include +#include +#include #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]); } } diff -r ace9a2ac3683 -r 3ab9bde9a605 usr/src/lib/libc/port/gen/getopt_long.c --- 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; } /*