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.
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.
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]
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
29 * Copyright 2009 Jason King. All rights reserved.
30 * Use is subject to license terms.
33 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
34 /* All Rights Reserved */
36 /* Copyright (c) 1987, 1988 Microsoft Corporation */
37 /* All Rights Reserved */
40 * List files or directories
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/mkdev.h>
70 #include <libnvpair.h>
71 #include <libcmdutils.h>
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.
92 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
96 * this flag has been added to manipulate the display of S instead of 'l' when
97 * the file is not a regular file and when group execution bit is off
99 #define LS_NOTREG 010000
103 * Date and time formats
105 * b --- abbreviated month name
107 * Y --- year in the form ccyy
108 * H --- hour(24-hour version)
112 * z --- time zone as hours displacement from UTC
113 * note that %F and %z are from the ISO C99 standard and are
114 * not present in older C libraries
116 #define FORMAT_OLD " %b %e %Y "
117 #define FORMAT_NEW " %b %e %H:%M "
118 #define FORMAT_LONG " %b %e %T %Y "
119 #define FORMAT_ISO_FULL " %%F %%T.%.09ld %%z "
120 #define FORMAT_ISO_LONG " %F %R "
121 #define FORMAT_ISO_NEW " %m-%d %H:%M "
122 #define FORMAT_ISO_OLD " %F "
126 #define NUMBER_WIDTH 40
130 dev_t dev
; /* directory items device number */
131 ino_t ino
; /* directory items inode number */
132 struct ditem
*parent
; /* dir items ptr to its parent's info */
134 /* Holds boolean extended system attributes */
138 /* Holds timestamp extended system attributes */
146 #define LSA_BOLD (1L << 0)
147 #define LSA_UNDERSCORE (1L << 1)
148 #define LSA_BLINK (1L << 2)
149 #define LSA_REVERSE (1L << 3)
150 #define LSA_CONCEALED (1L << 4)
152 /* these should be ordered most general to most specific */
153 typedef enum LS_CFTYPE
{
170 LS_STICKY_OTHER_WRITABLE
,
184 char lname
[MAXNAMLEN
]; /* used for filename in a directory */
185 char *namep
; /* for name in ls-command; */
187 char ltype
; /* filetype */
188 ino_t lnum
; /* inode number of file */
189 mode_t lflags
; /* 0777 bits used as r,w,x permissions */
190 nlink_t lnl
; /* number of links to file */
193 off_t lsize
; /* filesize or major/minor dev numbers */
194 blkcnt_t lblocks
; /* number of file blocks */
199 char *flinkto
; /* symbolic link contents */
200 char acl
; /* indicate there are additional acl entries */
201 int cycle
; /* cycle detected flag */
202 struct ditem
*ancinfo
; /* maintains ancestor info */
203 acl_t
*aclp
; /* ACL if present */
204 struct attrb
*exttr
; /* boolean extended system attributes */
205 struct attrtm
*extm
; /* timestamp extended system attributes */
206 ls_color_t
*color
; /* color for entry */
207 ls_color_t
*link_color
; /* color for symlink */
211 char *dc_name
; /* path name */
212 int cycle_detected
; /* cycle detected visiting this directory */
213 struct ditem
*myancinfo
; /* this directory's ancestry info */
214 struct dchain
*dc_next
; /* next directory in the chain */
218 * A numbuf_t is used when converting a number to a string representation
220 typedef char numbuf_t
[NUMBER_WIDTH
];
222 static struct dchain
*dfirst
; /* start of the dir chain */
223 static struct dchain
*cdfirst
; /* start of the current dir chain */
224 static struct dchain
*dtemp
; /* temporary - used for linking */
225 static char *curdir
; /* the current directory */
227 static int first
= 1; /* true if first line is not yet printed */
228 static int nfiles
= 0; /* number of flist entries in current use */
229 static int nargs
= 0; /* number of flist entries used for arguments */
230 static int maxfils
= 0; /* number of flist/lbuf entries allocated */
231 static int maxn
= 0; /* number of flist entries with lbufs asigned */
232 static int quantn
= 64; /* allocation growth quantum */
234 static struct lbuf
*nxtlbf
; /* ptr to next lbuf to be assigned */
235 static struct lbuf
**flist
; /* ptr to list of lbuf pointers */
236 static struct lbuf
*gstat(char *, int, struct ditem
*);
237 static char *getname(uid_t
);
238 static char *getgroup(gid_t
);
239 static char *makename(char *, char *);
240 static void pentry(struct lbuf
*);
241 static void column(void);
242 static void pmode(mode_t aflag
);
243 static void selection(int *);
244 static void new_line(void);
245 static void rddir(char *, struct ditem
*);
246 static int strcol(unsigned char *);
247 static void pem(struct lbuf
**, struct lbuf
**, int);
248 static void pdirectory(char *, int, int, int, struct ditem
*);
249 static struct cachenode
*findincache(struct cachenode
**, long);
250 static void csi_pprintf(unsigned char *);
251 static void pprintf(char *, char *);
252 static int compar(struct lbuf
**pp1
, struct lbuf
**pp2
);
253 static char *number_to_scaled_string(numbuf_t buf
,
254 unsigned long long number
,
256 static void record_ancestry(char *, struct stat
*, struct lbuf
*,
257 int, struct ditem
*);
258 static void ls_color_init(void);
259 static ls_color_t
*ls_color_find(const char *, mode_t
);
260 static void ls_start_color(ls_color_t
*);
261 static void ls_end_color(void);
279 static int rflg
= 1; /* init to 1 for special use in compar */
297 static int saflg
; /* boolean extended system attr. */
298 static int sacnt
; /* number of extended system attr. */
301 static int tmflg
; /* create time ext. system attr. */
309 static int err
= 0; /* Contains return code */
311 static int file_typeflg
;
312 static int noflist
= 0;
314 static uid_t lastuid
= (uid_t
)-1;
315 static gid_t lastgid
= (gid_t
)-1;
316 static char *lastuname
= NULL
;
317 static char *lastgname
= NULL
;
319 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
322 static uint64_t block_size
= 1;
323 static char *dotp
= ".";
325 static u_longlong_t tblocks
; /* number of blocks of files in a directory */
326 static time_t year
, now
;
328 static int num_cols
= 80;
330 static int filewidth
;
331 static int fixedwidth
;
335 static struct winsize win
;
337 /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
338 static const char *time_fmt_old
= FORMAT_OLD
; /* non-recent files */
339 static const char *time_fmt_new
= FORMAT_NEW
; /* recent files */
340 static int time_custom
; /* != 0 if a custom format */
341 static char time_buf
[FMTSIZE
]; /* array to hold day and time */
343 static int lsc_debug
;
344 static ls_color_t
*lsc_match
;
345 static ls_color_t
*lsc_colors
;
346 static size_t lsc_ncolors
;
347 static char *lsc_bold
;
348 static char *lsc_underline
;
349 static char *lsc_blink
;
350 static char *lsc_reverse
;
351 static char *lsc_concealed
;
352 static char *lsc_none
;
353 static char *lsc_setfg
;
354 static char *lsc_setbg
;
355 static ls_color_t
*lsc_orphan
;
357 #define NOTWORKINGDIR(d, l) (((l) < 2) || \
358 (strcmp((d) + (l) - 2, "/.") != 0))
360 #define NOTPARENTDIR(d, l) (((l) < 3) || \
361 (strcmp((d) + (l) - 3, "/..") != 0))
362 /* Extended system attributes support */
363 static int get_sysxattr(char *, struct lbuf
*);
364 static void set_sysattrb_display(char *, boolean_t
, struct lbuf
*);
365 static void set_sysattrtm_display(char *, struct lbuf
*);
366 static void format_time(time_t, time_t);
367 static void print_time(struct lbuf
*);
368 static void format_attrtime(struct lbuf
*);
369 static void *xmalloc(size_t, struct lbuf
*);
370 static void free_sysattr(struct lbuf
*);
371 static nvpair_t
*pair
;
372 static nvlist_t
*response
;
375 const struct option long_options
[] = {
376 { "all", no_argument
, NULL
, 'a' },
377 { "almost-all", no_argument
, NULL
, 'A' },
378 { "escape", no_argument
, NULL
, 'b' },
379 { "classify", no_argument
, NULL
, 'F' },
380 { "human-readable", no_argument
, NULL
, 'h' },
381 { "dereference", no_argument
, NULL
, 'L' },
382 { "dereference-command-line", no_argument
, NULL
, 'H' },
383 { "ignore-backups", no_argument
, NULL
, 'B' },
384 { "inode", no_argument
, NULL
, 'i' },
385 { "numeric-uid-gid", no_argument
, NULL
, 'n' },
386 { "no-group", no_argument
, NULL
, 'o' },
387 { "hide-control-chars", no_argument
, NULL
, 'q' },
388 { "reverse", no_argument
, NULL
, 'r' },
389 { "recursive", no_argument
, NULL
, 'R' },
390 { "size", no_argument
, NULL
, 's' },
391 { "width", required_argument
, NULL
, 'w' },
393 /* no short options for these */
394 { "block-size", required_argument
, NULL
, 0 },
395 { "full-time", no_argument
, NULL
, 0 },
396 { "si", no_argument
, NULL
, 0 },
397 { "color", optional_argument
, NULL
, 0 },
398 { "colour", optional_argument
, NULL
, 0},
399 { "file-type", no_argument
, NULL
, 0 },
400 { "time-style", required_argument
, NULL
, 0 },
406 main(int argc
, char *argv
[])
413 int option_index
= 0;
416 struct ditem
*myinfo
= NULL
;
418 (void) setlocale(LC_ALL
, "");
419 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
420 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
422 (void) textdomain(TEXT_DOMAIN
);
424 if (argv
[0][0] == '\0')
425 argc
= getargv("ls", &argv
, 0);
428 lb
.lmtime
.tv_sec
= time(NULL
);
429 lb
.lmtime
.tv_nsec
= 0;
430 year
= lb
.lmtime
.tv_sec
- 6L*30L*24L*60L*60L; /* 6 months ago */
431 now
= lb
.lmtime
.tv_sec
+ 60;
437 while ((c
= getopt_long(argc
, argv
,
438 "aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options
,
439 &option_index
)) != -1)
442 /* non-short options */
443 if (strcmp(long_options
[option_index
].name
,
445 strcmp(long_options
[option_index
].name
,
447 if (optarg
== NULL
||
448 strcmp(optarg
, "always") == 0 ||
449 strcmp(optarg
, "yes") == 0 ||
450 strcmp(optarg
, "force") == 0) {
456 if (strcmp(optarg
, "auto") == 0 ||
457 strcmp(optarg
, "tty") == 0 ||
458 strcmp(optarg
, "if-tty") == 0) {
459 if (isatty(1) == 1) {
466 if (strcmp(optarg
, "never") == 0 ||
467 strcmp(optarg
, "no") == 0 ||
468 strcmp(optarg
, "none") == 0) {
472 (void) fprintf(stderr
,
473 gettext("Invalid argument '%s' for "
474 "--color\n"), optarg
);
479 if (strcmp(long_options
[option_index
].name
,
486 if (strcmp(long_options
[option_index
].name
,
487 "block-size") == 0) {
488 size_t scale_len
= strlen(optarg
);
490 uint64_t kilo
= 1024;
493 if (scale_len
== 0) {
494 (void) fprintf(stderr
, gettext(
495 "Invalid block size \'%s\'\n"),
500 scale_c
= optarg
[scale_len
- 1];
501 if (scale_c
== 'B') {
502 /* need at least digit, scale, B */
504 (void) fprintf(stderr
, gettext(
505 "Invalid block size "
506 "\'%s\'\n"), optarg
);
510 scale_c
= optarg
[scale_len
- 2];
511 if (isdigit(scale_c
)) {
512 (void) fprintf(stderr
,
513 gettext("Invalid block size"
514 " \'%s\'\n"), optarg
);
518 * make optarg[scale_len - 1] point to
558 if (!isdigit(scale_c
)) {
559 (void) fprintf(stderr
,
560 gettext("Invalid character "
561 "following block size in "
562 "\'%s\'\n"), optarg
);
567 /* NULL out scale constant if present */
568 if (scale
> 1 && !isdigit(scale_c
))
569 optarg
[scale_len
- 1] = '\0';
571 /* Based on testing, this is what GNU ls does */
572 block_size
= strtoll(optarg
, NULL
, 0) * scale
;
573 if (block_size
< 1) {
574 (void) fprintf(stderr
,
575 gettext("Invalid block size "
576 "\'%s\'\n"), optarg
);
582 if (strcmp(long_options
[option_index
].name
,
591 if (strcmp(long_options
[option_index
].name
,
596 time_fmt_old
= FORMAT_ISO_FULL
;
597 time_fmt_new
= FORMAT_ISO_FULL
;
601 if (strcmp(long_options
[option_index
].name
,
602 "time-style") == 0) {
603 /* like -E, but doesn't imply -l */
604 if (strcmp(optarg
, "full-iso") == 0) {
608 time_fmt_old
= FORMAT_ISO_FULL
;
609 time_fmt_new
= FORMAT_ISO_FULL
;
612 if (strcmp(optarg
, "long-iso") == 0) {
616 time_fmt_old
= FORMAT_ISO_LONG
;
617 time_fmt_new
= FORMAT_ISO_LONG
;
620 if (strcmp(optarg
, "iso") == 0) {
624 time_fmt_old
= FORMAT_ISO_OLD
;
625 time_fmt_new
= FORMAT_ISO_NEW
;
628 /* should be the default */
629 if (strcmp(optarg
, "locale") == 0) {
630 time_fmt_old
= FORMAT_OLD
;
631 time_fmt_new
= FORMAT_NEW
;
634 if (optarg
[0] == '+') {
637 size_t timelen
= strlen(optarg
);
639 p
= strchr(optarg
, '\n');
644 * Time format requires a leading and
646 * Add room for 3 spaces + 2 nulls
647 * The + in optarg is replaced with
651 told
= malloc(timelen
);
657 (void) memset(told
, 0, timelen
);
659 (void) strlcat(told
, &optarg
[1],
661 (void) strlcat(told
, " ", timelen
);
666 tnew
= told
+ strlen(told
) + 1;
671 (void) strlcat(tnew
, p
,
673 (void) strlcat(tnew
, " ",
682 time_fmt_old
= (const char *)told
;
727 time_fmt_old
= FORMAT_LONG
;
728 time_fmt_new
= FORMAT_LONG
;
735 time_fmt_old
= FORMAT_ISO_FULL
;
736 time_fmt_new
= FORMAT_ISO_FULL
;
756 /* -H and -L are mutually exclusive */
775 /* -H and -L are mutually exclusive */
860 num_cols
= atoi(optarg
);
876 * -l has precedence over -@
890 if (optarg
!= NULL
) {
891 if (strcmp(optarg
, "c") == 0) {
894 } else if (strcmp(optarg
, "v") == 0) {
909 if (optarg
!= NULL
) {
910 if (strcmp(optarg
, "ctime") == 0) {
915 } else if (strcmp(optarg
, "atime") == 0) {
922 } else if (strcmp(optarg
, "mtime") == 0) {
929 } else if (strcmp(optarg
, "crtime") == 0) {
936 } else if (strcmp(optarg
, "all") == 0) {
957 (void) fprintf(stderr
, gettext(
958 "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
959 "%%[atime | crtime | ctime | mtime | all]"
990 if (!wflg
&& (Cflg
|| mflg
)) {
992 if ((clptr
= getenv("COLUMNS")) != NULL
)
993 num_cols
= atoi(clptr
);
996 if (ioctl(1, TIOCGWINSZ
, &win
) != -1)
997 num_cols
= (win
.ws_col
== 0 ? 80 : win
.ws_col
);
1003 * When certain options (-f, or -U and -1, and not -l, etc.) are
1004 * specified, don't cache each dirent as it's read. This 'noflist'
1005 * option is set when there's no need to cache those dirents; instead,
1006 * print them out as they're read.
1008 if ((Uflg
|| fflg
) && !Cflg
&& !lflg
&& !iflg
&& statreq
== 0)
1011 if (num_cols
< 20 || num_cols
> 1000)
1012 /* assume it is an error */
1015 /* allocate space for flist and the associated */
1016 /* data structures (lbufs) */
1018 if (((flist
= malloc(maxfils
* sizeof (struct lbuf
*))) == NULL
) ||
1019 ((nxtlbf
= malloc(quantn
* sizeof (struct lbuf
))) == NULL
)) {
1023 if ((amino
= (argc
-optind
)) == 0) {
1025 * case when no names are given
1026 * in ls-command and current
1027 * directory is to be used
1029 argv
[optind
] = dotp
;
1035 for (i
= 0; i
< (amino
? amino
: 1); i
++) {
1038 * If we are recursing, we need to make sure we don't
1039 * get into an endless loop. To keep track of the inodes
1040 * (actually, just the directories) visited, we
1041 * maintain a directory ancestry list for a file
1042 * hierarchy. As we go deeper into the hierarchy,
1043 * a parent directory passes its directory list
1044 * info (device id, inode number, and a pointer to
1045 * its parent) to each of its children. As we
1046 * process a child that is a directory, we save
1047 * its own personal directory list info. We then
1048 * check to see if the child has already been
1049 * processed by comparing its device id and inode
1050 * number from its own personal directory list info
1051 * to that of each of its ancestors. If there is a
1052 * match, then we know we've detected a cycle.
1056 * This is the first parent in this lineage
1057 * (first in a directory hierarchy), so
1058 * this parent's parent doesn't exist. We
1059 * only initialize myinfo when we are
1060 * recursing, otherwise it's not used.
1062 if ((myinfo
= (struct ditem
*)malloc(
1063 sizeof (struct ditem
))) == NULL
) {
1069 myinfo
->parent
= NULL
;
1074 width
= strcol((unsigned char *)argv
[optind
]);
1075 if (width
> filewidth
)
1078 if ((ep
= gstat((*argv
[optind
] ? argv
[optind
] : dotp
),
1079 1, myinfo
)) == NULL
) {
1086 ep
->ln
.namep
= (*argv
[optind
] ? argv
[optind
] : dotp
);
1087 ep
->lflags
|= ISARG
;
1089 nargs
++; /* count good arguments stored in flist */
1093 colwidth
= fixedwidth
+ filewidth
;
1095 qsort(flist
, (unsigned)nargs
, sizeof (struct lbuf
*),
1096 (int (*)(const void *, const void *))compar
);
1097 for (i
= 0; i
< nargs
; i
++) {
1098 if ((flist
[i
]->ltype
== 'd' && dflg
== 0) || fflg
)
1102 pem(&flist
[0], &flist
[i
], 0);
1103 for (; i
< nargs
; i
++) {
1104 pdirectory(flist
[i
]->ln
.namep
, Rflg
||
1105 (amino
> 1), nargs
, 0, flist
[i
]->ancinfo
);
1108 /* -R: print subdirectories found */
1109 while (dfirst
|| cdfirst
) {
1110 /* Place direct subdirs on front in right order */
1112 /* reverse cdfirst onto front of dfirst */
1114 cdfirst
= cdfirst
-> dc_next
;
1115 dtemp
-> dc_next
= dfirst
;
1118 /* take off first dir on dfirst & print it */
1120 dfirst
= dfirst
->dc_next
;
1121 pdirectory(dtemp
->dc_name
, 1, nargs
,
1122 dtemp
->cycle_detected
, dtemp
->myancinfo
);
1125 free(dtemp
->dc_name
);
1134 * pdirectory: print the directory name, labelling it if title is
1135 * nonzero, using lp as the place to start reading in the dir.
1138 pdirectory(char *name
, int title
, int lp
, int cdetect
, struct ditem
*myinfo
)
1149 (void) putc('\n', stdout
);
1154 * If there was a cycle detected, then notify and don't report
1159 curcol
+= printf(gettext("total %d"), 0);
1162 (void) fprintf(stderr
, gettext(
1163 "ls: cycle detected for %s\n"), name
);
1168 rddir(name
, myinfo
);
1169 if (nomocore
|| noflist
)
1171 if (fflg
== 0 && Uflg
== 0)
1172 qsort(&flist
[lp
], (unsigned)(nfiles
- lp
),
1173 sizeof (struct lbuf
*),
1174 (int (*)(const void *, const void *))compar
);
1176 for (j
= nfiles
- 1; j
>= lp
; j
--) {
1178 if (ap
->ltype
== 'd' && strcmp(ap
->ln
.lname
, ".") &&
1179 strcmp(ap
->ln
.lname
, "..")) {
1180 dp
= malloc(sizeof (struct dchain
));
1185 pname
= makename(curdir
, ap
->ln
.lname
);
1186 if ((dp
->dc_name
= strdup(pname
)) == NULL
) {
1190 dp
->cycle_detected
= ap
->cycle
;
1191 dp
->myancinfo
= ap
->ancinfo
;
1192 dp
->dc_next
= dfirst
;
1198 curcol
+= printf(gettext("total %llu"), tblocks
);
1201 pem(&flist
[lp
], &flist
[nfiles
], lflg
||sflg
);
1205 * pem: print 'em. Print a list of files (e.g. a directory) bounded
1209 pem(struct lbuf
**slp
, struct lbuf
**lp
, int tot_flag
)
1216 if (colwidth
<= num_cols
) {
1217 ncols
= num_cols
/ colwidth
;
1221 if (ncols
== 1 || mflg
|| xflg
|| !Cflg
) {
1222 for (ep
= slp
; ep
< lp
; ep
++)
1227 /* otherwise print -C columns */
1235 nrows
= (lp
- slp
- 1) / ncols
+ 1;
1236 for (i
= 0; i
< nrows
; i
++, row
++) {
1237 for (col
= 0; col
< ncols
; col
++) {
1238 ep
= slp
+ (nrows
* col
) + row
;
1247 * print one output entry;
1248 * if uid/gid is not found in the appropriate
1249 * file(passwd/group), then print uid/gid instead of
1253 pentry(struct lbuf
*ap
)
1257 char *dmark
= ""; /* Used if -p or -F option active */
1262 (void) printf("%s\n", (ap
->lflags
& ISARG
) ? ap
->ln
.namep
:
1271 curcol
+= printf("%llu ", (long long)p
->lnum
);
1273 curcol
+= printf("%10llu ", (long long)p
->lnum
);
1276 curcol
+= printf((mflg
&& !lflg
) ? "%lld " :
1277 (p
->lblocks
< 10000) ? "%4lld " : "%lld ",
1278 (p
->ltype
!= 'b' && p
->ltype
!= 'c') ?
1282 (void) putchar(p
->ltype
);
1286 /* ACL: additional access mode flag */
1287 (void) putchar(p
->acl
);
1290 curcol
+= printf("%3lu ", (ulong_t
)p
->lnl
);
1293 cp
= getname(p
->luid
);
1294 curcol
+= printf("%-8s ", cp
);
1296 curcol
+= printf("%-8lu ", (ulong_t
)p
->luid
);
1300 cp
= getgroup(p
->lgid
);
1301 curcol
+= printf("%-8s ", cp
);
1303 curcol
+= printf("%-8lu ", (ulong_t
)p
->lgid
);
1305 if (p
->ltype
== 'b' || p
->ltype
== 'c') {
1306 curcol
+= printf("%3u, %2u",
1307 (uint_t
)major((dev_t
)p
->lsize
),
1308 (uint_t
)minor((dev_t
)p
->lsize
));
1309 } else if (hflg
&& (p
->lsize
>= hscale
)) {
1310 curcol
+= printf("%7s",
1311 number_to_scaled_string(hbuf
, p
->lsize
, hscale
));
1313 uint64_t bsize
= p
->lsize
/ block_size
;
1316 * Round up only when using blocks > 1 byte, otherwise
1317 * 'normal' sizes display 1 byte too large.
1319 if (p
->lsize
% block_size
!= 0)
1322 curcol
+= printf("%7" PRIu64
, bsize
);
1324 format_time(p
->lmtime
.tv_sec
, p
->lmtime
.tv_nsec
);
1325 /* format extended system attribute time */
1329 curcol
+= printf("%s", time_buf
);
1333 * prevent both "->" and trailing marks
1337 if (pflg
&& p
->ltype
== 'd')
1340 if (Fflg
&& !(lflg
&& p
->flinkto
)) {
1341 if (p
->ltype
== 'd')
1343 else if (p
->ltype
== 'D')
1345 else if (p
->ltype
== 'p')
1347 else if (p
->ltype
== 'l')
1349 else if (p
->ltype
== 's')
1351 else if (!file_typeflg
&&
1352 (p
->lflags
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)))
1359 ls_start_color(p
->color
);
1361 if (p
->lflags
& ISARG
)
1367 csi_pprintf((unsigned char *)str
);
1369 if (lflg
&& p
->flinkto
) {
1372 csi_pprintf((unsigned char *)" -> ");
1374 ls_start_color(p
->link_color
);
1375 csi_pprintf((unsigned char *)p
->flinkto
);
1377 csi_pprintf((unsigned char *)dmark
);
1380 (void) printf("%s", str
);
1381 curcol
+= strcol((unsigned char *)str
);
1383 if (lflg
&& p
->flinkto
) {
1387 (void) printf("%s", str
);
1388 curcol
+= strcol((unsigned char *)str
);
1390 ls_start_color(p
->link_color
);
1391 (void) printf("%s", p
->flinkto
);
1392 curcol
+= strcol((unsigned char *)p
->flinkto
);
1394 (void) printf("%s", dmark
);
1395 curcol
+= strcol((unsigned char *)dmark
);
1402 /* Display extended system attributes */
1407 (void) printf(" \t{");
1408 if (p
->exttr
!= NULL
) {
1410 for (i
= 0; i
< sacnt
; i
++) {
1411 if (p
->exttr
[i
].name
!= NULL
)
1414 for (i
= 0; i
< sacnt
; i
++) {
1415 if (p
->exttr
[i
].name
!= NULL
) {
1416 (void) printf("%s", p
->exttr
[i
].name
);
1418 if (vopt
&& (k
!= 0))
1423 (void) printf("}\n");
1425 /* Display file timestamps and extended system attribute timestamps */
1426 if (tmflg
&& alltm
) {
1434 acl_printacl(p
->aclp
, num_cols
, Vflg
);
1437 /* Free extended system attribute lists */
1442 /* print various r,w,x permissions */
1446 /* these arrays are declared static to allow initializations */
1447 static int m0
[] = { 1, S_IRUSR
, 'r', '-' };
1448 static int m1
[] = { 1, S_IWUSR
, 'w', '-' };
1449 static int m2
[] = { 3, S_ISUID
|S_IXUSR
, 's', S_IXUSR
,
1450 'x', S_ISUID
, 'S', '-' };
1451 static int m3
[] = { 1, S_IRGRP
, 'r', '-' };
1452 static int m4
[] = { 1, S_IWGRP
, 'w', '-' };
1453 static int m5
[] = { 4, S_ISGID
|S_IXGRP
, 's', S_IXGRP
,
1454 'x', S_ISGID
|LS_NOTREG
, 'S',
1460 static int m6
[] = { 1, S_IROTH
, 'r', '-' };
1461 static int m7
[] = { 1, S_IWOTH
, 'w', '-' };
1462 static int m8
[] = { 3, S_ISVTX
|S_IXOTH
, 't', S_IXOTH
,
1463 'x', S_ISVTX
, 'T', '-'};
1465 static int *m
[] = { m0
, m1
, m2
, m3
, m4
, m5
, m6
, m7
, m8
};
1470 for (mp
= &m
[0]; mp
< &m
[sizeof (m
) / sizeof (m
[0])]; mp
++)
1475 selection(int *pairp
)
1481 if ((flags
& *pairp
) == *pairp
) {
1488 (void) putchar(*pairp
);
1493 * column: get to the beginning of the next column.
1501 (void) putc(',', stdout
);
1503 if (curcol
+ colwidth
+ 2 > num_cols
) {
1504 (void) putc('\n', stdout
);
1508 (void) putc(' ', stdout
);
1513 (void) putc('\n', stdout
);
1517 if ((curcol
/ colwidth
+ 2) * colwidth
> num_cols
) {
1518 (void) putc('\n', stdout
);
1523 (void) putc(' ', stdout
);
1525 } while (curcol
% colwidth
);
1533 (void) putc('\n', stdout
);
1539 * read each filename in directory dir and store its
1540 * status in flist[nfiles]
1541 * use makename() to form pathname dir/filename;
1544 rddir(char *dir
, struct ditem
*myinfo
)
1546 struct dirent
*dentry
;
1552 if ((dirf
= opendir(dir
)) == NULL
) {
1553 (void) fflush(stdout
);
1561 if ((dentry
= readdir(dirf
)) == NULL
)
1563 if (aflg
== 0 && dentry
->d_name
[0] == '.' &&
1565 dentry
->d_name
[1] == '\0' ||
1566 (dentry
->d_name
[1] == '.' &&
1567 dentry
->d_name
[2] == '\0')))
1569 * check for directory items '.', '..',
1570 * and items without valid inode-number;
1574 /* skip entries ending in ~ if -B was given */
1576 dentry
->d_name
[strlen(dentry
->d_name
) - 1] == '~')
1579 width
= strcol((unsigned char *)dentry
->d_name
);
1580 if (width
> filewidth
)
1583 ep
= gstat(makename(dir
, dentry
->d_name
), 0, myinfo
);
1589 ep
->lnum
= dentry
->d_ino
;
1590 for (j
= 0; dentry
->d_name
[j
] != '\0'; j
++)
1591 ep
->ln
.lname
[j
] = dentry
->d_name
[j
];
1592 ep
->ln
.lname
[j
] = '\0';
1595 * Since this entry doesn't need to be sorted
1596 * or further processed, print it right away.
1599 pem(&ep
, &ep
+ 1, 0);
1605 int sav_errno
= errno
;
1607 (void) fprintf(stderr
,
1608 gettext("ls: error reading directory %s: %s\n"),
1609 dir
, strerror(sav_errno
));
1611 (void) closedir(dirf
);
1612 colwidth
= fixedwidth
+ filewidth
;
1617 * Attaching a link to an inode's ancestors. Search
1618 * through the ancestors to check for cycles (an inode which
1619 * we have already tracked in this inodes ancestry). If a cycle
1620 * is detected, set the exit code and record the fact so that
1621 * it is reported at the right time when printing the directory.
1622 * In addition, set the exit code. Note: If the -a flag was
1623 * specified, we don't want to check for cycles for directories
1624 * ending in '/.' or '/..' unless they were specified on the
1628 record_ancestry(char *file
, struct stat
*pstatb
, struct lbuf
*rep
,
1629 int argfl
, struct ditem
*myparent
)
1632 struct ditem
*myinfo
;
1635 file_len
= strlen(file
);
1636 if (!aflg
|| argfl
|| (NOTWORKINGDIR(file
, file_len
) &&
1637 NOTPARENTDIR(file
, file_len
))) {
1639 * Add this inode's ancestry
1640 * info and insert it into the
1641 * ancestry list by pointing
1642 * back to its parent. We save
1643 * it (in rep) with the other info
1644 * we're gathering for this inode.
1646 if ((myinfo
= malloc(
1647 sizeof (struct ditem
))) == NULL
) {
1651 myinfo
->dev
= pstatb
->st_dev
;
1652 myinfo
->ino
= pstatb
->st_ino
;
1653 myinfo
->parent
= myparent
;
1654 rep
->ancinfo
= myinfo
;
1657 * If this node has the same device id and
1658 * inode number of one of its ancestors,
1659 * then we've detected a cycle.
1661 if (myparent
!= NULL
) {
1662 for (tptr
= myparent
; tptr
->parent
!= NULL
;
1663 tptr
= tptr
->parent
) {
1664 if ((tptr
->dev
== pstatb
->st_dev
) &&
1665 (tptr
->ino
== pstatb
->st_ino
)) {
1667 * Cycle detected for this
1668 * directory. Record the fact
1669 * it is a cycle so we don't
1670 * try to process this
1671 * directory as we are
1672 * walking through the
1673 * list of directories.
1685 * Do re-calculate the mode for group for ACE_T type of acls.
1686 * This is because, if the server's FS happens to be UFS, supporting
1687 * POSIX ACL's, then it does a special calculation of group mode
1688 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1690 * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1691 * algorithm is used for the group mode calculation only.
1692 * What is modified here from the algorithm is that only the
1693 * entries with flags ACE_GROUP are considered. For each entry
1694 * with ACE_GROUP flag, the first occurance of a specific access
1695 * is checked if it is allowed.
1696 * We are not interested in perms for user and other, as they
1697 * were taken from st_mode value.
1698 * We are not interested in a_who field of ACE, as we need just
1699 * unix mode bits for the group.
1702 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP)
1703 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1706 grp_mask_to_mode(struct lbuf
*p
)
1708 int mode
= 0, seen
= 0;
1712 acl_t
*acep
= p
->aclp
;
1714 acecnt
= acl_cnt(acep
);
1715 for (ap
= (ace_t
*)acl_data(acep
); acecnt
--; ap
++) {
1717 if (ap
->a_type
!= ACE_ACCESS_ALLOWED_ACE_TYPE
&&
1718 ap
->a_type
!= ACE_ACCESS_DENIED_ACE_TYPE
)
1721 if (ap
->a_flags
& ACE_INHERIT_ONLY_ACE
)
1725 * if it is first group@ or first everyone@
1726 * for each of read, write and execute, then
1727 * that will be the group mode bit.
1729 flags
= ap
->a_flags
& ACE_TYPE_FLAGS
;
1730 if (flags
== OWNED_GROUP
|| (flags
== ACE_IDENTIFIER_GROUP
&&
1731 ap
->a_who
== p
->lgid
) || flags
== ACE_EVERYONE
) {
1732 if (ap
->a_access_mask
& ACE_READ_DATA
) {
1733 if (!(seen
& S_IRGRP
)) {
1735 if (IS_TYPE_ALLOWED(ap
->a_type
))
1739 if (ap
->a_access_mask
& ACE_WRITE_DATA
) {
1740 if (!(seen
& S_IWGRP
)) {
1742 if (IS_TYPE_ALLOWED(ap
->a_type
))
1746 if (ap
->a_access_mask
& ACE_EXECUTE
) {
1747 if (!(seen
& S_IXGRP
)) {
1749 if (IS_TYPE_ALLOWED(ap
->a_type
))
1759 * get status of file and recomputes tblocks;
1760 * argfl = 1 if file is a name in ls-command and = 0
1761 * for filename in a directory whose name is an
1762 * argument in the command;
1763 * stores a pointer in flist[nfiles] and
1764 * returns that pointer;
1765 * returns NULL if failed;
1767 static struct lbuf
*
1768 gstat(char *file
, int argfl
, struct ditem
*myparent
)
1770 struct stat statb
, statb1
;
1774 int (*statf
)() = ((Lflg
) || (Hflg
&& argfl
)) ? stat
: lstat
;
1778 o_mode_t groupperm
, mask
;
1779 int grouppermfound
, maskfound
;
1784 if (nfiles
>= maxfils
) {
1786 * all flist/lbuf pair assigned files, time to get some
1790 if (((flist
= reallocarray(flist
, maxfils
,
1791 sizeof (struct lbuf
*))) == NULL
) ||
1792 ((nxtlbf
= malloc(quantn
*
1793 sizeof (struct lbuf
))) == NULL
)) {
1801 * nfiles is reset to nargs for each directory
1802 * that is given as an argument maxn is checked
1803 * to prevent the assignment of an lbuf to a flist entry
1804 * that already has one assigned.
1806 if (nfiles
>= maxn
) {
1808 flist
[nfiles
++] = rep
;
1811 rep
= flist
[nfiles
++];
1814 /* Clear the lbuf */
1815 (void) memset(rep
, 0, sizeof (struct lbuf
));
1818 * When noflist is set, none of the extra information about the dirent
1819 * will be printed, so omit remaining initialization of this lbuf
1820 * as well as the stat(2) call.
1822 if (!argfl
&& noflist
)
1825 /* Initialize non-zero members */
1827 rep
->lat
.tv_sec
= time(NULL
);
1828 rep
->lct
.tv_sec
= time(NULL
);
1829 rep
->lmt
.tv_sec
= time(NULL
);
1831 if (argfl
|| statreq
) {
1839 if ((*statf
)(file
, &statb
) < 0) {
1840 if (argfl
|| errno
!= ENOENT
||
1841 (Lflg
&& lstat(file
, &statb
) == 0)) {
1843 * Avoid race between readdir and lstat.
1844 * Print error message in case of dangling link.
1854 * If -H was specified, and the file linked to was
1855 * not a directory, then we need to get the info
1856 * for the symlink itself.
1858 if ((Hflg
) && (argfl
) &&
1859 ((statb
.st_mode
& S_IFMT
) != S_IFDIR
)) {
1860 if (lstat(file
, &statb
) < 0) {
1866 rep
->lnum
= statb
.st_ino
;
1867 rep
->lsize
= statb
.st_size
;
1868 rep
->lblocks
= statb
.st_blocks
;
1870 rep
->color
= ls_color_find(file
, statb
.st_mode
);
1872 switch (statb
.st_mode
& S_IFMT
) {
1876 record_ancestry(file
, &statb
, rep
,
1882 rep
->lsize
= (off_t
)statb
.st_rdev
;
1886 rep
->lsize
= (off_t
)statb
.st_rdev
;
1896 /* symbolic links may not have ACLs, so elide acl() */
1897 if ((Lflg
== 0) || (Hflg
== 0) ||
1898 ((Hflg
) && (!argfl
))) {
1902 if (lflg
|| colorflg
) {
1903 cc
= readlink(file
, buf
, BUFSIZ
);
1908 * follow the symbolic link
1909 * to generate the appropriate
1910 * Fflg marker for the object
1911 * eg, /bin -> /sym/bin/
1914 if (Fflg
|| pflg
|| colorflg
)
1915 error
= stat(file
, &statb1
);
1927 if ((Fflg
|| pflg
) && error
>= 0) {
1928 switch (statb1
.st_mode
& S_IFMT
) {
1942 if ((statb1
.st_mode
& ~S_IFMT
) &
1943 (S_IXUSR
|S_IXGRP
| S_IXOTH
))
1949 rep
->flinkto
= strdup(buf
);
1950 if (rep
->flinkto
== NULL
) {
1959 * ls /sym behaves differently from ls /sym/
1960 * when /sym is a symbolic link. This is fixed
1961 * when explicit arguments are specified.
1965 /* Do not follow a symlink when -F is specified */
1966 if ((!argfl
) || (argfl
&& Fflg
) ||
1967 (stat(file
, &statb1
) < 0))
1969 /* Follow a symlink when -F is specified */
1970 if (!argfl
|| stat(file
, &statb1
) < 0)
1973 if ((statb1
.st_mode
& S_IFMT
) == S_IFDIR
) {
1976 rep
->lsize
= statb1
.st_size
;
1978 record_ancestry(file
, &statb
, rep
,
1996 rep
->lflags
= statb
.st_mode
& ~S_IFMT
;
1998 if (!S_ISREG(statb
.st_mode
))
1999 rep
->lflags
|= LS_NOTREG
;
2001 rep
->luid
= statb
.st_uid
;
2002 rep
->lgid
= statb
.st_gid
;
2003 rep
->lnl
= statb
.st_nlink
;
2004 if (uflg
|| (tmflg
&& atm
))
2005 rep
->lmtime
= statb
.st_atim
;
2006 else if (cflg
|| (tmflg
&& ctm
))
2007 rep
->lmtime
= statb
.st_ctim
;
2009 rep
->lmtime
= statb
.st_mtim
;
2010 rep
->lat
= statb
.st_atim
;
2011 rep
->lct
= statb
.st_ctim
;
2012 rep
->lmt
= statb
.st_mtim
;
2014 /* ACL: check acl entries count */
2017 error
= acl_get(file
, 0, &rep
->aclp
);
2019 (void) fprintf(stderr
,
2020 gettext("ls: can't read ACL on %s: %s\n"),
2021 file
, acl_strerror(error
));
2030 ((acl_flags(rep
->aclp
) & ACL_IS_TRIVIAL
) == 0)) {
2033 * Special handling for ufs aka aclent_t ACL's
2035 if (acl_type(rep
->aclp
) == ACLENT_T
) {
2037 * For files with non-trivial acls, the
2038 * effective group permissions are the
2039 * intersection of the GROUP_OBJ value
2040 * and the CLASS_OBJ (acl mask) value.
2041 * Determine both the GROUP_OBJ and
2042 * CLASS_OBJ for this file and insert
2043 * the logical AND of those two values
2044 * in the group permissions field
2045 * of the lflags value for this file.
2049 * Until found in acl list, assume
2050 * maximum permissions for both group
2051 * a nd mask. (Just in case the acl
2052 * lacks either value for some reason.)
2058 aclcnt
= acl_cnt(rep
->aclp
);
2060 (aclent_t
*)acl_data(rep
->aclp
);
2062 if (tp
->a_type
== GROUP_OBJ
) {
2063 groupperm
= tp
->a_perm
;
2067 if (tp
->a_type
== CLASS_OBJ
) {
2071 if (grouppermfound
&& maskfound
)
2076 /* reset all the group bits */
2077 rep
->lflags
&= ~S_IRWXG
;
2080 * Now set them to the logical AND of
2081 * the GROUP_OBJ permissions and the
2085 rep
->lflags
|= (groupperm
& mask
) << 3;
2087 } else if (acl_type(rep
->aclp
) == ACE_T
) {
2089 mode
= grp_mask_to_mode(rep
);
2090 rep
->lflags
&= ~S_IRWXG
;
2091 rep
->lflags
|= mode
;
2095 if (!vflg
&& !Vflg
&& rep
->aclp
) {
2096 acl_free(rep
->aclp
);
2100 if (atflg
&& pathconf(file
, _PC_XATTR_EXISTS
) == 1)
2106 /* mask ISARG and other file-type bits */
2108 if (rep
->ltype
!= 'b' && rep
->ltype
!= 'c')
2109 tblocks
+= rep
->lblocks
;
2111 /* Get extended system attributes */
2113 if ((saflg
|| (tmflg
&& crtm
) || (tmflg
&& alltm
)) &&
2114 (sysattr_support(file
, _PC_SATTR_EXISTS
) == 1)) {
2117 sacnt
= attr_count();
2119 * Allocate 'sacnt' size array to hold extended
2120 * system attribute name (verbose) or respective
2121 * symbol represenation (compact).
2123 rep
->exttr
= xmalloc(sacnt
* sizeof (struct attrb
),
2126 /* initialize boolean attribute list */
2127 for (i
= 0; i
< sacnt
; i
++)
2128 rep
->exttr
[i
].name
= NULL
;
2129 if (get_sysxattr(file
, rep
) != 0) {
2130 (void) fprintf(stderr
,
2131 gettext("ls:Failed to retrieve "
2132 "extended system attribute from "
2134 rep
->exttr
[0].name
= xmalloc(2, rep
);
2135 (void) strlcpy(rep
->exttr
[0].name
, "?", 2);
2143 * returns pathname of the form dir/file;
2144 * dir and file are null-terminated strings.
2147 makename(char *dir
, char *file
)
2150 * PATH_MAX is the maximum length of a path name.
2151 * MAXNAMLEN is the maximum length of any path name component.
2152 * Allocate space for both, plus the '/' in the middle
2153 * and the null character at the end.
2154 * dfile is static as this is returned by makename().
2156 static char dfile
[PATH_MAX
+ 1 + MAXNAMLEN
+ 1];
2163 if (dp
> dfile
&& *(dp
- 1) != '/')
2179 #define NMAX (sizeof (utmp.ut_name))
2180 #define SCPYN(a, b) (void) strncpy(a, b, NMAX)
2183 struct cachenode
{ /* this struct must be zeroed before using */
2184 struct cachenode
*lesschild
; /* subtree whose entries < val */
2185 struct cachenode
*grtrchild
; /* subtree whose entries > val */
2186 long val
; /* the uid or gid of this entry */
2187 int initted
; /* name has been filled in */
2188 char name
[NMAX
+1]; /* the string that val maps to */
2190 static struct cachenode
*names
, *groups
;
2192 static struct cachenode
*
2193 findincache(struct cachenode
**head
, long val
)
2195 struct cachenode
**parent
= head
;
2196 struct cachenode
*c
= *parent
;
2199 if (val
== c
->val
) {
2202 } else if (val
< c
->val
) {
2203 parent
= &c
->lesschild
;
2206 parent
= &c
->grtrchild
;
2211 /* not in the cache, make a new entry for it */
2212 c
= calloc(1, sizeof (struct cachenode
));
2223 * get name from cache, or passwd file for a given uid;
2224 * lastuid is set to uid.
2229 struct passwd
*pwent
;
2230 struct cachenode
*c
;
2232 if ((uid
== lastuid
) && lastuname
)
2235 c
= findincache(&names
, uid
);
2236 if (c
->initted
== 0) {
2237 if ((pwent
= getpwuid(uid
)) != NULL
) {
2238 SCPYN(&c
->name
[0], pwent
->pw_name
);
2240 (void) sprintf(&c
->name
[0], "%-8u", (int)uid
);
2245 lastuname
= &c
->name
[0];
2250 * get name from cache, or group file for a given gid;
2251 * lastgid is set to gid.
2256 struct group
*grent
;
2257 struct cachenode
*c
;
2259 if ((gid
== lastgid
) && lastgname
)
2262 c
= findincache(&groups
, gid
);
2263 if (c
->initted
== 0) {
2264 if ((grent
= getgrgid(gid
)) != NULL
) {
2265 SCPYN(&c
->name
[0], grent
->gr_name
);
2267 (void) sprintf(&c
->name
[0], "%-8u", (int)gid
);
2272 lastgname
= &c
->name
[0];
2276 /* return >0 if item pointed by pp2 should appear first */
2278 compar(struct lbuf
**pp1
, struct lbuf
**pp2
)
2280 struct lbuf
*p1
, *p2
;
2286 * compare two names in ls-command one of which is file
2287 * and the other is a directory;
2288 * this portion is not used for comparing files within
2289 * a directory name of ls-command;
2291 if (p1
->lflags
&ISARG
&& p1
->ltype
== 'd') {
2292 if (!(p2
->lflags
&ISARG
&& p2
->ltype
== 'd'))
2295 if (p2
->lflags
&ISARG
&& p2
->ltype
== 'd')
2300 if (p2
->lmtime
.tv_sec
> p1
->lmtime
.tv_sec
)
2302 else if (p2
->lmtime
.tv_sec
< p1
->lmtime
.tv_sec
)
2304 /* times are equal to the sec, check nsec */
2305 if (p2
->lmtime
.tv_nsec
> p1
->lmtime
.tv_nsec
)
2307 else if (p2
->lmtime
.tv_nsec
< p1
->lmtime
.tv_nsec
)
2309 /* if times are equal, fall through and sort by name */
2312 * The size stored in lsize can be either the
2313 * size or the major minor number (in the case of
2314 * block and character special devices). If it's
2315 * a major minor number, then the size is considered
2316 * to be zero and we want to fall through and sort
2317 * by name. In addition, if the size of p2 is equal
2318 * to the size of p1 we want to fall through and
2321 off_t p1size
= (p1
->ltype
== 'b') ||
2322 (p1
->ltype
== 'c') ? 0 : p1
->lsize
;
2323 off_t p2size
= (p2
->ltype
== 'b') ||
2324 (p2
->ltype
== 'c') ? 0 : p2
->lsize
;
2325 if (p2size
> p1size
) {
2327 } else if (p2size
< p1size
) {
2330 /* Sizes are equal, fall through and sort by name. */
2332 return (rflg
* strcoll(
2333 p1
->lflags
& ISARG
? p1
->ln
.namep
: p1
->ln
.lname
,
2334 p2
->lflags
&ISARG
? p2
->ln
.namep
: p2
->ln
.lname
));
2338 pprintf(char *s1
, char *s2
)
2340 csi_pprintf((unsigned char *)s1
);
2341 csi_pprintf((unsigned char *)s2
);
2345 csi_pprintf(unsigned char *s
)
2354 if (!qflg
&& !bflg
) {
2355 for (cp
= s
; *cp
!= '\0'; cp
++) {
2356 (void) putchar(*cp
);
2362 for (cp
= s
; *cp
; ) {
2363 if (isascii(c
= *cp
)) {
2369 (void) putc('\\', stdout
);
2370 c
= '0' + ((*cp
>> 6) & 07);
2371 (void) putc(c
, stdout
);
2372 c
= '0' + ((*cp
>> 3) & 07);
2373 (void) putc(c
, stdout
);
2374 c
= '0' + (*cp
& 07);
2379 (void) putc(c
, stdout
);
2383 if ((c_len
= mbtowc(&pcode
, (char *)cp
, MB_LEN_MAX
)) <= 0) {
2388 if ((p_col
= wcwidth(pcode
)) > 0) {
2389 (void) putwchar(pcode
);
2396 for (i
= 0; i
< c_len
; i
++) {
2401 (void) putc('\\', stdout
);
2402 c
= '0' + ((*cp
>> 6) & 07);
2403 (void) putc(c
, stdout
);
2404 c
= '0' + ((*cp
>> 3) & 07);
2405 (void) putc(c
, stdout
);
2406 c
= '0' + (*cp
& 07);
2409 (void) putc(c
, stdout
);
2416 strcol(unsigned char *s1
)
2431 if ((len
= mbtowc(&wc
, (char *)s1
, MB_LEN_MAX
)) <= 0) {
2437 if ((w_col
= wcwidth(wc
)) < 0)
2446 * Convert an unsigned long long to a string representation and place the
2447 * result in the caller-supplied buffer.
2449 * The number provided is a size in bytes. The number is first
2450 * converted to an integral multiple of 'scale' bytes. This new
2451 * number is then scaled down until it is small enough to be in a good
2452 * human readable format, i.e. in the range 0 thru scale-1. If the
2453 * number used to derive the final number is not a multiple of scale, and
2454 * the final number has only a single significant digit, we compute
2455 * tenths of units to provide a second significant digit.
2457 * The value "(unsigned long long)-1" is a special case and is always
2458 * converted to "-1".
2460 * A pointer to the caller-supplied buffer is returned.
2463 number_to_scaled_string(
2464 numbuf_t buf
, /* put the result here */
2465 unsigned long long number
, /* convert this number */
2468 unsigned long long save
;
2469 /* Measurement: kilo, mega, giga, tera, peta, exa */
2470 char *uom
= "KMGTPE";
2472 if ((long long)number
== (long long)-1) {
2473 (void) strlcpy(buf
, "-1", sizeof (numbuf_t
));
2478 number
= number
/ scale
;
2481 * Now we have number as a count of scale units.
2482 * If no further scaling is necessary, we round up as appropriate.
2484 * The largest value number could have had entering the routine is
2485 * 16 Exabytes, so running off the end of the uom array should
2486 * never happen. We check for that, though, as a guard against
2487 * a breakdown elsewhere in the algorithm.
2489 if (number
< (unsigned long long)scale
) {
2490 if ((save
% scale
) >= (unsigned long long)(scale
/ 2)) {
2491 if (++number
== (unsigned long long)scale
) {
2497 while ((number
>= (unsigned long long)scale
) && (*uom
!= 'E')) {
2498 uom
++; /* next unit of measurement */
2501 * If we're over half way to the next unit of
2502 * 'scale' bytes (which means we should round
2503 * up), then adding half of 'scale' prior to
2504 * the division will push us into that next
2505 * unit of scale when we perform the division
2507 number
= (number
+ (scale
/ 2)) / scale
;
2511 /* check if we should output a decimal place after the point */
2512 if ((save
/ scale
) < 10) {
2513 /* snprintf() will round for us */
2514 float fnum
= (float)save
/ scale
;
2515 (void) snprintf(buf
, sizeof (numbuf_t
), "%2.1f%c",
2518 (void) snprintf(buf
, sizeof (numbuf_t
), "%4llu%c",
2524 /* Get extended system attributes and set the display */
2527 get_sysxattr(char *fname
, struct lbuf
*rep
)
2535 if ((error
= getattrat(AT_FDCWD
, XATTR_VIEW_READWRITE
, fname
,
2537 perror("ls:getattrat");
2542 * Allocate 'sacnt' size array to hold extended timestamp
2543 * system attributes and initialize the array.
2545 rep
->extm
= xmalloc(sacnt
* sizeof (struct attrtm
), rep
);
2546 for (i
= 0; i
< sacnt
; i
++) {
2547 rep
->extm
[i
].stm
= 0;
2548 rep
->extm
[i
].nstm
= 0;
2549 rep
->extm
[i
].name
= NULL
;
2551 while ((pair
= nvlist_next_nvpair(response
, pair
)) != NULL
) {
2552 name
= nvpair_name(pair
);
2553 type
= nvpair_type(pair
);
2554 if (type
== DATA_TYPE_BOOLEAN_VALUE
) {
2555 error
= nvpair_value_boolean_value(pair
, &value
);
2557 (void) fprintf(stderr
,
2558 gettext("nvpair_value_boolean_value "
2559 "failed: error = %d\n"), error
);
2563 set_sysattrb_display(name
, value
, rep
);
2565 } else if (type
== DATA_TYPE_UINT64_ARRAY
) {
2567 set_sysattrtm_display(name
, rep
);
2571 nvlist_free(response
);
2575 /* Set extended system attribute boolean display */
2578 set_sysattrb_display(char *name
, boolean_t val
, struct lbuf
*rep
)
2584 fattr
= name_to_attr(name
);
2585 if (fattr
!= F_ATTR_INVAL
&& fattr
< sacnt
) {
2589 rep
->exttr
[fattr
].name
= xmalloc(len
+ 1, rep
);
2590 (void) strlcpy(rep
->exttr
[fattr
].name
, name
,
2593 rep
->exttr
[fattr
].name
= xmalloc(len
+ 3, rep
);
2594 (void) snprintf(rep
->exttr
[fattr
].name
, len
+ 3,
2598 opt
= attr_to_option(fattr
);
2601 rep
->exttr
[fattr
].name
= xmalloc(len
+ 1, rep
);
2603 (void) strlcpy(rep
->exttr
[fattr
].name
,
2606 (void) strlcpy(rep
->exttr
[fattr
].name
,
2613 /* Set extended system attribute timestamp display */
2616 set_sysattrtm_display(char *name
, struct lbuf
*rep
)
2623 if (nvpair_value_uint64_array(pair
, &value
, &nelem
) == 0) {
2624 if (*value
!= (uintptr_t)NULL
) {
2627 while (rep
->extm
[i
].stm
!= 0 && i
< sacnt
)
2629 rep
->extm
[i
].stm
= value
[0];
2630 rep
->extm
[i
].nstm
= value
[1];
2631 rep
->extm
[i
].name
= xmalloc(len
+ 1, rep
);
2632 (void) strlcpy(rep
->extm
[i
].name
, name
, len
+ 1);
2638 format_time(time_t sec
, time_t nsec
)
2640 const char *fstr
= time_fmt_new
;
2641 char fmt_buf
[FMTSIZE
];
2644 (void) snprintf(fmt_buf
, FMTSIZE
, fstr
, nsec
);
2645 (void) strftime(time_buf
, sizeof (time_buf
), fmt_buf
,
2650 if (sec
< year
|| sec
> now
)
2651 fstr
= time_fmt_old
;
2653 /* if a custom time was specified, shouldn't be localized */
2654 (void) strftime(time_buf
, sizeof (time_buf
),
2655 (time_custom
== 0) ? dcgettext(NULL
, fstr
, LC_TIME
) : fstr
,
2660 format_attrtime(struct lbuf
*p
)
2665 if (p
->extm
!= NULL
) {
2666 for (i
= 0; i
< sacnt
; i
++) {
2667 if (p
->extm
[i
].name
!= NULL
) {
2675 const char *old_save
= time_fmt_old
;
2676 const char *new_save
= time_fmt_new
;
2678 /* Eflg always sets format to FORMAT_ISO_FULL */
2679 if (!Eflg
&& !time_custom
) {
2680 time_fmt_old
= FORMAT_OLD
;
2681 time_fmt_new
= FORMAT_NEW
;
2684 format_time((time_t)p
->extm
[i
].stm
, (time_t)p
->extm
[i
].nstm
);
2686 time_fmt_old
= old_save
;
2687 time_fmt_new
= new_save
;
2692 print_time(struct lbuf
*p
)
2694 const char *old_save
= time_fmt_old
;
2695 const char *new_save
= time_fmt_new
;
2700 time_fmt_old
= FORMAT_LONG
;
2701 time_fmt_new
= FORMAT_LONG
;
2705 format_time(p
->lat
.tv_sec
, p
->lat
.tv_nsec
);
2706 (void) printf(" timestamp: atime %s\n", time_buf
);
2707 format_time(p
->lct
.tv_sec
, p
->lct
.tv_nsec
);
2708 (void) printf(" timestamp: ctime %s\n", time_buf
);
2709 format_time(p
->lmt
.tv_sec
, p
->lmt
.tv_nsec
);
2710 (void) printf(" timestamp: mtime %s\n", time_buf
);
2711 if (p
->extm
!= NULL
) {
2712 while (p
->extm
[i
].nstm
!= 0 && i
< sacnt
) {
2713 format_time(p
->extm
[i
].stm
, p
->extm
[i
].nstm
);
2714 if (p
->extm
[i
].name
!= NULL
) {
2715 (void) printf(" timestamp:"
2717 p
->extm
[i
].name
, time_buf
);
2723 time_fmt_old
= old_save
;
2724 time_fmt_new
= new_save
;
2728 * Check if color definition applies to entry, returns 1 if yes, 0 if no
2731 color_match(const char *fname
, mode_t mode
, ls_color_t
*color
)
2733 switch (color
->ftype
) {
2736 size_t fname_len
, sfx_len
;
2738 fname_len
= strlen(fname
);
2739 sfx_len
= strlen(color
->sfx
);
2740 if (sfx_len
> fname_len
)
2743 if (strcmp(color
->sfx
, fname
+ fname_len
- sfx_len
) == 0)
2753 return (S_ISREG(mode
));
2756 return (S_ISDIR(mode
));
2759 return (S_ISLNK(mode
));
2762 return (S_ISFIFO(mode
));
2765 return (S_ISSOCK(mode
));
2768 return (S_ISDOOR(mode
));
2771 return (S_ISBLK(mode
));
2774 return (S_ISCHR(mode
));
2777 return (S_ISPORT(mode
));
2780 /* this is tested for by gstat */
2784 return (!S_ISLNK(mode
) && (mode
& S_ISUID
));
2787 return (!S_ISLNK(mode
) && (mode
& S_ISGID
));
2789 case LS_STICKY_OTHER_WRITABLE
:
2790 return (!S_ISLNK(mode
) && (mode
& (S_IWOTH
|S_ISVTX
)));
2792 case LS_OTHER_WRITABLE
:
2793 return (!S_ISLNK(mode
) && (mode
& S_IWOTH
));
2796 return (!S_ISLNK(mode
) && (mode
& S_ISVTX
));
2799 return (!S_ISLNK(mode
) && (mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)));
2806 dump_color(ls_color_t
*c
)
2811 (void) printf("\n\ttype: ");
2814 (void) printf("LS_NORMAL");
2817 (void) printf("LS_FILE");
2820 (void) printf("LS_EXEC");
2823 (void) printf("LS_DIR");
2826 (void) printf("LS_LINK");
2830 (void) printf("LS_FIFO");
2834 (void) printf("LS_SOCK");
2838 (void) printf("LS_DOOR");
2842 (void) printf("LS_BLK");
2846 (void) printf("LS_CHR");
2850 (void) printf("LS_PORT");
2854 (void) printf("LS_STICKY");
2858 (void) printf("LS_ORPHAN");
2862 (void) printf("LS_SETGID");
2866 (void) printf("LS_SETUID");
2869 case LS_OTHER_WRITABLE
:
2870 (void) printf("LS_OTHER_WRITABLE");
2873 case LS_STICKY_OTHER_WRITABLE
:
2874 (void) printf("LS_STICKY_OTHER_WRITABLE");
2878 (void) printf("LS_PAT\n");
2879 (void) printf("\tpattern: %s", c
->sfx
);
2882 (void) printf("\n");
2883 (void) printf("\tattr: %d\n", c
->attr
);
2884 (void) printf("\tfg: %d\n", c
->fg
);
2885 (void) printf("\tbg: %d\n", c
->bg
);
2886 (void) printf("\t");
2890 ls_color_find(const char *fname
, mode_t mode
)
2895 * Colors are sorted from most general lsc_colors[0] to most specific
2896 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with
2897 * most specific color rule and work towards most general.
2899 for (i
= lsc_ncolors
- 1; i
>= 0; --i
)
2900 if (color_match(fname
, mode
, &lsc_colors
[i
]))
2901 return (&lsc_colors
[i
]);
2907 ls_tprint(char *str
, long int p1
, long int p2
, long int p3
, long int p4
,
2908 long int p5
, long int p6
, long int p7
, long int p8
, long int p9
)
2915 s
= tparm(str
, p1
, p2
, p3
, p4
, p5
, p6
, p7
, p8
, p9
);
2922 ls_start_color(ls_color_t
*c
)
2930 if (c
->attr
& LSA_BOLD
)
2931 ls_tprint(lsc_bold
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2932 if (c
->attr
& LSA_UNDERSCORE
)
2933 ls_tprint(lsc_underline
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2934 if (c
->attr
& LSA_BLINK
)
2935 ls_tprint(lsc_blink
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2936 if (c
->attr
& LSA_REVERSE
)
2937 ls_tprint(lsc_reverse
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2938 if (c
->attr
& LSA_CONCEALED
)
2939 ls_tprint(lsc_concealed
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2940 if (c
->attr
== LSA_NONE
)
2941 ls_tprint(lsc_none
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2944 ls_tprint(lsc_setfg
, c
->fg
, 0, 0, 0, 0, 0, 0, 0, 0);
2946 ls_tprint(lsc_setbg
, c
->bg
, 0, 0, 0, 0, 0, 0, 0, 0);
2952 ls_tprint(lsc_none
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2954 dump_color(lsc_match
);
2958 new_color_entry(char *colorstr
)
2960 static const struct {
2964 { "no", LS_NORMAL
},
2973 { "or", LS_ORPHAN
},
2974 { "su", LS_SETUID
},
2975 { "sg", LS_SETGID
},
2976 { "tw", LS_STICKY_OTHER_WRITABLE
},
2977 { "ow", LS_OTHER_WRITABLE
},
2978 { "st", LS_STICKY
},
2988 p
= strtok_r(colorstr
, "=", &lasts
);
2995 lsc_colors
[lsc_ncolors
].ftype
= LS_PAT
;
2996 /* don't include the * in the suffix */
2997 if ((lsc_colors
[lsc_ncolors
].sfx
= strdup(p
+ 1)) == NULL
) {
3002 lsc_colors
[lsc_ncolors
].sfx
= NULL
;
3004 for (i
= 0; type_map
[i
].s
!= NULL
; ++i
) {
3005 if (strncmp(type_map
[i
].s
, p
, 2) == 0)
3009 /* ignore unknown file types */
3010 if (type_map
[i
].s
== NULL
)
3013 lsc_colors
[lsc_ncolors
].ftype
= type_map
[i
].stype
;
3017 lsc_colors
[lsc_ncolors
].fg
= -1;
3018 lsc_colors
[lsc_ncolors
].bg
= -1;
3019 for (p
= strtok_r(NULL
, ";", &lasts
); p
!= NULL
;
3020 p
= strtok_r(NULL
, ";", &lasts
)) {
3021 color
= strtol(p
, NULL
, 10);
3032 attr
|= LSA_UNDERSCORE
;
3038 attr
|= LSA_REVERSE
;
3041 attr
|= LSA_CONCEALED
;
3049 lsc_colors
[lsc_ncolors
].fg
= color
- 30;
3051 lsc_colors
[lsc_ncolors
].bg
= color
- 40;
3054 lsc_colors
[lsc_ncolors
].attr
= attr
;
3059 ls_color_compare(const void *p1
, const void *p2
)
3061 const ls_color_t
*c1
= (const ls_color_t
*)p1
;
3062 const ls_color_t
*c2
= (const ls_color_t
*)p2
;
3064 int ret
= c1
->ftype
- c2
->ftype
;
3069 if (c1
->ftype
!= LS_PAT
)
3072 return (strcmp(c1
->sfx
, c2
->sfx
));
3078 static char *default_colorstr
= "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3079 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3080 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3081 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3082 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3083 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3084 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3085 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3086 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3087 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3088 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3096 (void) setupterm(NULL
, 1, &termret
);
3100 if ((p
= getenv("LS_COLORS")) == NULL
)
3101 p
= default_colorstr
;
3102 colorstr
= strdup(p
);
3103 if (colorstr
== NULL
)
3107 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors
3108 * if there are invalid entries passed in the string (they are ignored)
3111 for (p
= strchr(colorstr
, ':'); p
!= NULL
&& *p
!= '\0';
3112 p
= strchr(++p
, ':'))
3115 lsc_colors
= calloc(color_sz
, sizeof (ls_color_t
));
3116 if (lsc_colors
== NULL
) {
3121 for (p
= strtok_r(colorstr
, ":", &lasts
);
3122 p
!= NULL
&& lsc_ncolors
< color_sz
;
3123 p
= strtok_r(NULL
, ":", &lasts
))
3126 qsort((void *)lsc_colors
, lsc_ncolors
, sizeof (ls_color_t
),
3129 for (i
= 0; i
< lsc_ncolors
; ++i
)
3130 if (lsc_colors
[i
].ftype
== LS_ORPHAN
) {
3131 lsc_orphan
= &lsc_colors
[i
];
3135 if ((lsc_bold
= tigetstr("bold")) == (char *)-1)
3138 if ((lsc_underline
= tigetstr("smul")) == (char *)-1)
3139 lsc_underline
= NULL
;
3141 if ((lsc_blink
= tigetstr("blink")) == (char *)-1)
3144 if ((lsc_reverse
= tigetstr("rev")) == (char *)-1)
3147 if ((lsc_concealed
= tigetstr("prot")) == (char *)-1)
3148 lsc_concealed
= NULL
;
3150 if ((lsc_none
= tigetstr("sgr0")) == (char *)-1)
3153 if ((lsc_setfg
= tigetstr("setaf")) == (char *)-1)
3156 if ((lsc_setbg
= tigetstr("setab")) == (char *)-1)
3159 if (getenv("_LS_COLOR_DEBUG") != NULL
) {
3163 for (i
= 0; i
< lsc_ncolors
; ++i
)
3164 dump_color(&lsc_colors
[i
]);
3170 /* Free extended system attribute lists */
3173 free_sysattr(struct lbuf
*p
)
3177 if (p
->exttr
!= NULL
) {
3178 for (i
= 0; i
< sacnt
; i
++) {
3179 free(p
->exttr
[i
].name
);
3183 if (p
->extm
!= NULL
) {
3184 for (i
= 0; i
< sacnt
; i
++) {
3185 free(p
->extm
[i
].name
);
3191 /* Allocate extended system attribute list */
3194 xmalloc(size_t size
, struct lbuf
*p
)
3196 if ((p
= malloc(size
)) == NULL
) {
3199 nvlist_free(response
);