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 2017 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 "
129 dev_t dev
; /* directory items device number */
130 ino_t ino
; /* directory items inode number */
131 struct ditem
*parent
; /* dir items ptr to its parent's info */
133 /* Holds boolean extended system attributes */
137 /* Holds timestamp extended system attributes */
145 #define LSA_BOLD (1L << 0)
146 #define LSA_UNDERSCORE (1L << 1)
147 #define LSA_BLINK (1L << 2)
148 #define LSA_REVERSE (1L << 3)
149 #define LSA_CONCEALED (1L << 4)
151 /* these should be ordered most general to most specific */
152 typedef enum LS_CFTYPE
{
169 LS_STICKY_OTHER_WRITABLE
,
183 char lname
[MAXNAMLEN
]; /* used for filename in a directory */
184 char *namep
; /* for name in ls-command; */
186 char ltype
; /* filetype */
187 ino_t lnum
; /* inode number of file */
188 mode_t lflags
; /* 0777 bits used as r,w,x permissions */
189 nlink_t lnl
; /* number of links to file */
192 off_t lsize
; /* filesize or major/minor dev numbers */
193 blkcnt_t lblocks
; /* number of file blocks */
198 char *flinkto
; /* symbolic link contents */
199 char acl
; /* indicate there are additional acl entries */
200 int cycle
; /* cycle detected flag */
201 struct ditem
*ancinfo
; /* maintains ancestor info */
202 acl_t
*aclp
; /* ACL if present */
203 struct attrb
*exttr
; /* boolean extended system attributes */
204 struct attrtm
*extm
; /* timestamp extended system attributes */
205 ls_color_t
*color
; /* color for entry */
206 ls_color_t
*link_color
; /* color for symlink */
210 char *dc_name
; /* path name */
211 int cycle_detected
; /* cycle detected visiting this directory */
212 struct ditem
*myancinfo
; /* this directory's ancestry info */
213 struct dchain
*dc_next
; /* next directory in the chain */
216 static struct dchain
*dfirst
; /* start of the dir chain */
217 static struct dchain
*cdfirst
; /* start of the current dir chain */
218 static struct dchain
*dtemp
; /* temporary - used for linking */
219 static char *curdir
; /* the current directory */
221 static int first
= 1; /* true if first line is not yet printed */
222 static int nfiles
= 0; /* number of flist entries in current use */
223 static int nargs
= 0; /* number of flist entries used for arguments */
224 static int maxfils
= 0; /* number of flist/lbuf entries allocated */
225 static int maxn
= 0; /* number of flist entries with lbufs asigned */
226 static int quantn
= 64; /* allocation growth quantum */
228 static struct lbuf
*nxtlbf
; /* ptr to next lbuf to be assigned */
229 static struct lbuf
**flist
; /* ptr to list of lbuf pointers */
230 static struct lbuf
*gstat(char *, int, struct ditem
*);
231 static char *getname(uid_t
);
232 static char *getgroup(gid_t
);
233 static char *makename(char *, char *);
234 static void pentry(struct lbuf
*);
235 static void column(void);
236 static void pmode(mode_t aflag
);
237 static void selection(int *);
238 static void new_line(void);
239 static void rddir(char *, struct ditem
*);
240 static int strcol(unsigned char *);
241 static void pem(struct lbuf
**, struct lbuf
**, int);
242 static void pdirectory(char *, int, int, int, struct ditem
*);
243 static struct cachenode
*findincache(struct cachenode
**, long);
244 static void csi_pprintf(unsigned char *);
245 static void pprintf(char *, char *);
246 static int compar(struct lbuf
**pp1
, struct lbuf
**pp2
);
247 static void record_ancestry(char *, struct stat
*, struct lbuf
*,
248 int, struct ditem
*);
249 static void ls_color_init(void);
250 static ls_color_t
*ls_color_find(const char *, mode_t
);
251 static void ls_start_color(ls_color_t
*);
252 static void ls_end_color(void);
270 static int rflg
= 1; /* init to 1 for special use in compar */
288 static int saflg
; /* boolean extended system attr. */
289 static int sacnt
; /* number of extended system attr. */
292 static int tmflg
; /* create time ext. system attr. */
298 static uint_t nicenum_flags
;
300 static int err
= 0; /* Contains return code */
302 static int file_typeflg
;
303 static int noflist
= 0;
305 static uid_t lastuid
= (uid_t
)-1;
306 static gid_t lastgid
= (gid_t
)-1;
307 static char *lastuname
= NULL
;
308 static char *lastgname
= NULL
;
310 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
313 static uint64_t block_size
= 1;
314 static char *dotp
= ".";
316 static u_longlong_t tblocks
; /* number of blocks of files in a directory */
317 static time_t year
, now
;
319 static int num_cols
= 80;
321 static int filewidth
;
322 static int fixedwidth
;
326 static struct winsize win
;
328 /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
329 static const char *time_fmt_old
= FORMAT_OLD
; /* non-recent files */
330 static const char *time_fmt_new
= FORMAT_NEW
; /* recent files */
331 static int time_custom
; /* != 0 if a custom format */
332 static char time_buf
[FMTSIZE
]; /* array to hold day and time */
334 static int lsc_debug
;
335 static ls_color_t
*lsc_match
;
336 static ls_color_t
*lsc_colors
;
337 static size_t lsc_ncolors
;
338 static char *lsc_bold
;
339 static char *lsc_underline
;
340 static char *lsc_blink
;
341 static char *lsc_reverse
;
342 static char *lsc_concealed
;
343 static char *lsc_none
;
344 static char *lsc_setfg
;
345 static char *lsc_setbg
;
346 static ls_color_t
*lsc_orphan
;
348 #define NOTWORKINGDIR(d, l) (((l) < 2) || \
349 (strcmp((d) + (l) - 2, "/.") != 0))
351 #define NOTPARENTDIR(d, l) (((l) < 3) || \
352 (strcmp((d) + (l) - 3, "/..") != 0))
353 /* Extended system attributes support */
354 static int get_sysxattr(char *, struct lbuf
*);
355 static void set_sysattrb_display(char *, boolean_t
, struct lbuf
*);
356 static void set_sysattrtm_display(char *, struct lbuf
*);
357 static void format_time(time_t, time_t);
358 static void print_time(struct lbuf
*);
359 static void format_attrtime(struct lbuf
*);
360 static void *xmalloc(size_t, struct lbuf
*);
361 static void free_sysattr(struct lbuf
*);
362 static nvpair_t
*pair
;
363 static nvlist_t
*response
;
366 const struct option long_options
[] = {
367 { "all", no_argument
, NULL
, 'a' },
368 { "almost-all", no_argument
, NULL
, 'A' },
369 { "escape", no_argument
, NULL
, 'b' },
370 { "classify", no_argument
, NULL
, 'F' },
371 { "human-readable", no_argument
, NULL
, 'h' },
372 { "dereference", no_argument
, NULL
, 'L' },
373 { "dereference-command-line", no_argument
, NULL
, 'H' },
374 { "ignore-backups", no_argument
, NULL
, 'B' },
375 { "inode", no_argument
, NULL
, 'i' },
376 { "numeric-uid-gid", no_argument
, NULL
, 'n' },
377 { "no-group", no_argument
, NULL
, 'o' },
378 { "hide-control-chars", no_argument
, NULL
, 'q' },
379 { "reverse", no_argument
, NULL
, 'r' },
380 { "recursive", no_argument
, NULL
, 'R' },
381 { "size", no_argument
, NULL
, 's' },
382 { "width", required_argument
, NULL
, 'w' },
384 /* no short options for these */
385 { "block-size", required_argument
, NULL
, 0 },
386 { "full-time", no_argument
, NULL
, 0 },
387 { "si", no_argument
, NULL
, 0 },
388 { "color", optional_argument
, NULL
, 0 },
389 { "colour", optional_argument
, NULL
, 0},
390 { "file-type", no_argument
, NULL
, 0 },
391 { "time-style", required_argument
, NULL
, 0 },
397 main(int argc
, char *argv
[])
404 int option_index
= 0;
407 struct ditem
*myinfo
= NULL
;
409 (void) setlocale(LC_ALL
, "");
410 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
411 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
413 (void) textdomain(TEXT_DOMAIN
);
415 if (argv
[0][0] == '\0')
416 argc
= getargv("ls", &argv
, 0);
419 lb
.lmtime
.tv_sec
= time(NULL
);
420 lb
.lmtime
.tv_nsec
= 0;
421 year
= lb
.lmtime
.tv_sec
- 6L*30L*24L*60L*60L; /* 6 months ago */
422 now
= lb
.lmtime
.tv_sec
+ 60;
428 while ((c
= getopt_long(argc
, argv
,
429 "aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options
,
430 &option_index
)) != -1)
433 /* non-short options */
434 if (strcmp(long_options
[option_index
].name
,
436 strcmp(long_options
[option_index
].name
,
438 if (optarg
== NULL
||
439 strcmp(optarg
, "always") == 0 ||
440 strcmp(optarg
, "yes") == 0 ||
441 strcmp(optarg
, "force") == 0) {
447 if (strcmp(optarg
, "auto") == 0 ||
448 strcmp(optarg
, "tty") == 0 ||
449 strcmp(optarg
, "if-tty") == 0) {
450 if (isatty(1) == 1) {
457 if (strcmp(optarg
, "never") == 0 ||
458 strcmp(optarg
, "no") == 0 ||
459 strcmp(optarg
, "none") == 0) {
463 (void) fprintf(stderr
,
464 gettext("Invalid argument '%s' for "
465 "--color\n"), optarg
);
470 if (strcmp(long_options
[option_index
].name
,
473 nicenum_flags
|= NN_DIVISOR_1000
;
477 if (strcmp(long_options
[option_index
].name
,
478 "block-size") == 0) {
479 size_t scale_len
= strlen(optarg
);
481 uint64_t kilo
= 1024;
484 if (scale_len
== 0) {
485 (void) fprintf(stderr
, gettext(
486 "Invalid block size \'%s\'\n"),
491 scale_c
= optarg
[scale_len
- 1];
492 if (scale_c
== 'B') {
493 /* need at least digit, scale, B */
495 (void) fprintf(stderr
, gettext(
496 "Invalid block size "
497 "\'%s\'\n"), optarg
);
501 scale_c
= optarg
[scale_len
- 2];
502 if (isdigit(scale_c
)) {
503 (void) fprintf(stderr
,
504 gettext("Invalid block size"
505 " \'%s\'\n"), optarg
);
509 * make optarg[scale_len - 1] point to
549 if (!isdigit(scale_c
)) {
550 (void) fprintf(stderr
,
551 gettext("Invalid character "
552 "following block size in "
553 "\'%s\'\n"), optarg
);
558 /* NULL out scale constant if present */
559 if (scale
> 1 && !isdigit(scale_c
))
560 optarg
[scale_len
- 1] = '\0';
562 /* Based on testing, this is what GNU ls does */
563 block_size
= strtoll(optarg
, NULL
, 0) * scale
;
564 if (block_size
< 1) {
565 (void) fprintf(stderr
,
566 gettext("Invalid block size "
567 "\'%s\'\n"), optarg
);
573 if (strcmp(long_options
[option_index
].name
,
582 if (strcmp(long_options
[option_index
].name
,
587 time_fmt_old
= FORMAT_ISO_FULL
;
588 time_fmt_new
= FORMAT_ISO_FULL
;
592 if (strcmp(long_options
[option_index
].name
,
593 "time-style") == 0) {
594 /* like -E, but doesn't imply -l */
595 if (strcmp(optarg
, "full-iso") == 0) {
599 time_fmt_old
= FORMAT_ISO_FULL
;
600 time_fmt_new
= FORMAT_ISO_FULL
;
603 if (strcmp(optarg
, "long-iso") == 0) {
607 time_fmt_old
= FORMAT_ISO_LONG
;
608 time_fmt_new
= FORMAT_ISO_LONG
;
611 if (strcmp(optarg
, "iso") == 0) {
615 time_fmt_old
= FORMAT_ISO_OLD
;
616 time_fmt_new
= FORMAT_ISO_NEW
;
619 /* should be the default */
620 if (strcmp(optarg
, "locale") == 0) {
621 time_fmt_old
= FORMAT_OLD
;
622 time_fmt_new
= FORMAT_NEW
;
625 if (optarg
[0] == '+') {
628 size_t timelen
= strlen(optarg
);
630 p
= strchr(optarg
, '\n');
635 * Time format requires a leading and
637 * Add room for 3 spaces + 2 nulls
638 * The + in optarg is replaced with
642 told
= malloc(timelen
);
648 (void) memset(told
, 0, timelen
);
650 (void) strlcat(told
, &optarg
[1],
652 (void) strlcat(told
, " ", timelen
);
657 tnew
= told
+ strlen(told
) + 1;
662 (void) strlcat(tnew
, p
,
664 (void) strlcat(tnew
, " ",
673 time_fmt_old
= (const char *)told
;
718 time_fmt_old
= FORMAT_LONG
;
719 time_fmt_new
= FORMAT_LONG
;
726 time_fmt_old
= FORMAT_ISO_FULL
;
727 time_fmt_new
= FORMAT_ISO_FULL
;
746 /* -H and -L are mutually exclusive */
765 /* -H and -L are mutually exclusive */
850 num_cols
= atoi(optarg
);
866 * -l has precedence over -@
880 if (optarg
!= NULL
) {
881 if (strcmp(optarg
, "c") == 0) {
884 } else if (strcmp(optarg
, "v") == 0) {
899 if (optarg
!= NULL
) {
900 if (strcmp(optarg
, "ctime") == 0) {
905 } else if (strcmp(optarg
, "atime") == 0) {
912 } else if (strcmp(optarg
, "mtime") == 0) {
919 } else if (strcmp(optarg
, "crtime") == 0) {
926 } else if (strcmp(optarg
, "all") == 0) {
947 (void) fprintf(stderr
, gettext(
948 "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
949 "%%[atime | crtime | ctime | mtime | all]"
980 if (!wflg
&& (Cflg
|| mflg
)) {
982 if ((clptr
= getenv("COLUMNS")) != NULL
)
983 num_cols
= atoi(clptr
);
986 if (ioctl(1, TIOCGWINSZ
, &win
) != -1)
987 num_cols
= (win
.ws_col
== 0 ? 80 : win
.ws_col
);
993 * When certain options (-f, or -U and -1, and not -l, etc.) are
994 * specified, don't cache each dirent as it's read. This 'noflist'
995 * option is set when there's no need to cache those dirents; instead,
996 * print them out as they're read.
998 if ((Uflg
|| fflg
) && !Cflg
&& !lflg
&& !iflg
&& statreq
== 0)
1001 if (num_cols
< 20 || num_cols
> 1000)
1002 /* assume it is an error */
1005 /* allocate space for flist and the associated */
1006 /* data structures (lbufs) */
1008 if (((flist
= malloc(maxfils
* sizeof (struct lbuf
*))) == NULL
) ||
1009 ((nxtlbf
= malloc(quantn
* sizeof (struct lbuf
))) == NULL
)) {
1013 if ((amino
= (argc
-optind
)) == 0) {
1015 * case when no names are given
1016 * in ls-command and current
1017 * directory is to be used
1019 argv
[optind
] = dotp
;
1025 for (i
= 0; i
< (amino
? amino
: 1); i
++) {
1028 * If we are recursing, we need to make sure we don't
1029 * get into an endless loop. To keep track of the inodes
1030 * (actually, just the directories) visited, we
1031 * maintain a directory ancestry list for a file
1032 * hierarchy. As we go deeper into the hierarchy,
1033 * a parent directory passes its directory list
1034 * info (device id, inode number, and a pointer to
1035 * its parent) to each of its children. As we
1036 * process a child that is a directory, we save
1037 * its own personal directory list info. We then
1038 * check to see if the child has already been
1039 * processed by comparing its device id and inode
1040 * number from its own personal directory list info
1041 * to that of each of its ancestors. If there is a
1042 * match, then we know we've detected a cycle.
1046 * This is the first parent in this lineage
1047 * (first in a directory hierarchy), so
1048 * this parent's parent doesn't exist. We
1049 * only initialize myinfo when we are
1050 * recursing, otherwise it's not used.
1052 if ((myinfo
= (struct ditem
*)malloc(
1053 sizeof (struct ditem
))) == NULL
) {
1059 myinfo
->parent
= NULL
;
1064 width
= strcol((unsigned char *)argv
[optind
]);
1065 if (width
> filewidth
)
1068 if ((ep
= gstat((*argv
[optind
] ? argv
[optind
] : dotp
),
1069 1, myinfo
)) == NULL
) {
1076 ep
->ln
.namep
= (*argv
[optind
] ? argv
[optind
] : dotp
);
1077 ep
->lflags
|= ISARG
;
1079 nargs
++; /* count good arguments stored in flist */
1083 colwidth
= fixedwidth
+ filewidth
;
1085 qsort(flist
, (unsigned)nargs
, sizeof (struct lbuf
*),
1086 (int (*)(const void *, const void *))compar
);
1087 for (i
= 0; i
< nargs
; i
++) {
1088 if ((flist
[i
]->ltype
== 'd' && dflg
== 0) || fflg
)
1092 pem(&flist
[0], &flist
[i
], 0);
1093 for (; i
< nargs
; i
++) {
1094 pdirectory(flist
[i
]->ln
.namep
, Rflg
||
1095 (amino
> 1), nargs
, 0, flist
[i
]->ancinfo
);
1098 /* -R: print subdirectories found */
1099 while (dfirst
|| cdfirst
) {
1100 /* Place direct subdirs on front in right order */
1102 /* reverse cdfirst onto front of dfirst */
1104 cdfirst
= cdfirst
-> dc_next
;
1105 dtemp
-> dc_next
= dfirst
;
1108 /* take off first dir on dfirst & print it */
1110 dfirst
= dfirst
->dc_next
;
1111 pdirectory(dtemp
->dc_name
, 1, nargs
,
1112 dtemp
->cycle_detected
, dtemp
->myancinfo
);
1115 free(dtemp
->dc_name
);
1124 * pdirectory: print the directory name, labelling it if title is
1125 * nonzero, using lp as the place to start reading in the dir.
1128 pdirectory(char *name
, int title
, int lp
, int cdetect
, struct ditem
*myinfo
)
1139 (void) putc('\n', stdout
);
1144 * If there was a cycle detected, then notify and don't report
1149 curcol
+= printf(gettext("total %d"), 0);
1152 (void) fprintf(stderr
, gettext(
1153 "ls: cycle detected for %s\n"), name
);
1158 rddir(name
, myinfo
);
1159 if (nomocore
|| noflist
)
1161 if (fflg
== 0 && Uflg
== 0)
1162 qsort(&flist
[lp
], (unsigned)(nfiles
- lp
),
1163 sizeof (struct lbuf
*),
1164 (int (*)(const void *, const void *))compar
);
1166 for (j
= nfiles
- 1; j
>= lp
; j
--) {
1168 if (ap
->ltype
== 'd' && strcmp(ap
->ln
.lname
, ".") &&
1169 strcmp(ap
->ln
.lname
, "..")) {
1170 dp
= malloc(sizeof (struct dchain
));
1175 pname
= makename(curdir
, ap
->ln
.lname
);
1176 if ((dp
->dc_name
= strdup(pname
)) == NULL
) {
1180 dp
->cycle_detected
= ap
->cycle
;
1181 dp
->myancinfo
= ap
->ancinfo
;
1182 dp
->dc_next
= dfirst
;
1188 curcol
+= printf(gettext("total %llu"), tblocks
);
1191 pem(&flist
[lp
], &flist
[nfiles
], lflg
||sflg
);
1195 * pem: print 'em. Print a list of files (e.g. a directory) bounded
1199 pem(struct lbuf
**slp
, struct lbuf
**lp
, int tot_flag
)
1206 if (colwidth
<= num_cols
) {
1207 ncols
= num_cols
/ colwidth
;
1211 if (ncols
== 1 || mflg
|| xflg
|| !Cflg
) {
1212 for (ep
= slp
; ep
< lp
; ep
++)
1217 /* otherwise print -C columns */
1225 nrows
= (lp
- slp
- 1) / ncols
+ 1;
1226 for (i
= 0; i
< nrows
; i
++, row
++) {
1227 for (col
= 0; col
< ncols
; col
++) {
1228 ep
= slp
+ (nrows
* col
) + row
;
1237 * print one output entry;
1238 * if uid/gid is not found in the appropriate
1239 * file(passwd/group), then print uid/gid instead of
1243 pentry(struct lbuf
*ap
)
1246 char *dmark
= ""; /* Used if -p or -F option active */
1251 (void) printf("%s\n", (ap
->lflags
& ISARG
) ? ap
->ln
.namep
:
1260 curcol
+= printf("%llu ", (long long)p
->lnum
);
1262 curcol
+= printf("%10llu ", (long long)p
->lnum
);
1265 curcol
+= printf((mflg
&& !lflg
) ? "%lld " :
1266 (p
->lblocks
< 10000) ? "%4lld " : "%lld ",
1267 (p
->ltype
!= 'b' && p
->ltype
!= 'c') ?
1271 (void) putchar(p
->ltype
);
1275 /* ACL: additional access mode flag */
1276 (void) putchar(p
->acl
);
1279 curcol
+= printf("%3lu ", (ulong_t
)p
->lnl
);
1282 cp
= getname(p
->luid
);
1283 curcol
+= printf("%-8s ", cp
);
1285 curcol
+= printf("%-8lu ", (ulong_t
)p
->luid
);
1289 cp
= getgroup(p
->lgid
);
1290 curcol
+= printf("%-8s ", cp
);
1292 curcol
+= printf("%-8lu ", (ulong_t
)p
->lgid
);
1294 if (p
->ltype
== 'b' || p
->ltype
== 'c') {
1295 curcol
+= printf("%3u, %2u",
1296 (uint_t
)major((dev_t
)p
->lsize
),
1297 (uint_t
)minor((dev_t
)p
->lsize
));
1299 char numbuf
[NN_NUMBUF_SZ
];
1301 nicenum_scale(p
->lsize
, 1, numbuf
, sizeof (numbuf
),
1304 curcol
+= printf("%7s", numbuf
);
1306 uint64_t bsize
= p
->lsize
/ block_size
;
1309 * Round up only when using blocks > 1 byte, otherwise
1310 * 'normal' sizes display 1 byte too large.
1312 if (p
->lsize
% block_size
!= 0)
1315 curcol
+= printf("%7" PRIu64
, bsize
);
1317 format_time(p
->lmtime
.tv_sec
, p
->lmtime
.tv_nsec
);
1318 /* format extended system attribute time */
1322 curcol
+= printf("%s", time_buf
);
1326 * prevent both "->" and trailing marks
1330 if (pflg
&& p
->ltype
== 'd')
1333 if (Fflg
&& !(lflg
&& p
->flinkto
)) {
1334 if (p
->ltype
== 'd')
1336 else if (p
->ltype
== 'D')
1338 else if (p
->ltype
== 'p')
1340 else if (p
->ltype
== 'l')
1342 else if (p
->ltype
== 's')
1344 else if (!file_typeflg
&&
1345 (p
->lflags
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)))
1352 ls_start_color(p
->color
);
1354 if (p
->lflags
& ISARG
)
1360 csi_pprintf((unsigned char *)str
);
1362 if (lflg
&& p
->flinkto
) {
1365 csi_pprintf((unsigned char *)" -> ");
1367 ls_start_color(p
->link_color
);
1368 csi_pprintf((unsigned char *)p
->flinkto
);
1370 csi_pprintf((unsigned char *)dmark
);
1373 (void) printf("%s", str
);
1374 curcol
+= strcol((unsigned char *)str
);
1376 if (lflg
&& p
->flinkto
) {
1380 (void) printf("%s", str
);
1381 curcol
+= strcol((unsigned char *)str
);
1383 ls_start_color(p
->link_color
);
1384 (void) printf("%s", p
->flinkto
);
1385 curcol
+= strcol((unsigned char *)p
->flinkto
);
1387 (void) printf("%s", dmark
);
1388 curcol
+= strcol((unsigned char *)dmark
);
1395 /* Display extended system attributes */
1400 (void) printf(" \t{");
1401 if (p
->exttr
!= NULL
) {
1403 for (i
= 0; i
< sacnt
; i
++) {
1404 if (p
->exttr
[i
].name
!= NULL
)
1407 for (i
= 0; i
< sacnt
; i
++) {
1408 if (p
->exttr
[i
].name
!= NULL
) {
1409 (void) printf("%s", p
->exttr
[i
].name
);
1411 if (vopt
&& (k
!= 0))
1416 (void) printf("}\n");
1418 /* Display file timestamps and extended system attribute timestamps */
1419 if (tmflg
&& alltm
) {
1427 acl_printacl(p
->aclp
, num_cols
, Vflg
);
1430 /* Free extended system attribute lists */
1435 /* print various r,w,x permissions */
1439 /* these arrays are declared static to allow initializations */
1440 static int m0
[] = { 1, S_IRUSR
, 'r', '-' };
1441 static int m1
[] = { 1, S_IWUSR
, 'w', '-' };
1442 static int m2
[] = { 3, S_ISUID
|S_IXUSR
, 's', S_IXUSR
,
1443 'x', S_ISUID
, 'S', '-' };
1444 static int m3
[] = { 1, S_IRGRP
, 'r', '-' };
1445 static int m4
[] = { 1, S_IWGRP
, 'w', '-' };
1446 static int m5
[] = { 4, S_ISGID
|S_IXGRP
, 's', S_IXGRP
,
1447 'x', S_ISGID
|LS_NOTREG
, 'S',
1453 static int m6
[] = { 1, S_IROTH
, 'r', '-' };
1454 static int m7
[] = { 1, S_IWOTH
, 'w', '-' };
1455 static int m8
[] = { 3, S_ISVTX
|S_IXOTH
, 't', S_IXOTH
,
1456 'x', S_ISVTX
, 'T', '-'};
1458 static int *m
[] = { m0
, m1
, m2
, m3
, m4
, m5
, m6
, m7
, m8
};
1463 for (mp
= &m
[0]; mp
< &m
[sizeof (m
) / sizeof (m
[0])]; mp
++)
1468 selection(int *pairp
)
1474 if ((flags
& *pairp
) == *pairp
) {
1481 (void) putchar(*pairp
);
1486 * column: get to the beginning of the next column.
1494 (void) putc(',', stdout
);
1496 if (curcol
+ colwidth
+ 2 > num_cols
) {
1497 (void) putc('\n', stdout
);
1501 (void) putc(' ', stdout
);
1506 (void) putc('\n', stdout
);
1510 if ((curcol
/ colwidth
+ 2) * colwidth
> num_cols
) {
1511 (void) putc('\n', stdout
);
1516 (void) putc(' ', stdout
);
1518 } while (curcol
% colwidth
);
1526 (void) putc('\n', stdout
);
1532 * read each filename in directory dir and store its
1533 * status in flist[nfiles]
1534 * use makename() to form pathname dir/filename;
1537 rddir(char *dir
, struct ditem
*myinfo
)
1539 struct dirent
*dentry
;
1545 if ((dirf
= opendir(dir
)) == NULL
) {
1546 (void) fflush(stdout
);
1554 if ((dentry
= readdir(dirf
)) == NULL
)
1556 if (aflg
== 0 && dentry
->d_name
[0] == '.' &&
1558 dentry
->d_name
[1] == '\0' ||
1559 (dentry
->d_name
[1] == '.' &&
1560 dentry
->d_name
[2] == '\0')))
1562 * check for directory items '.', '..',
1563 * and items without valid inode-number;
1567 /* skip entries ending in ~ if -B was given */
1569 dentry
->d_name
[strlen(dentry
->d_name
) - 1] == '~')
1572 width
= strcol((unsigned char *)dentry
->d_name
);
1573 if (width
> filewidth
)
1576 ep
= gstat(makename(dir
, dentry
->d_name
), 0, myinfo
);
1582 ep
->lnum
= dentry
->d_ino
;
1583 for (j
= 0; dentry
->d_name
[j
] != '\0'; j
++)
1584 ep
->ln
.lname
[j
] = dentry
->d_name
[j
];
1585 ep
->ln
.lname
[j
] = '\0';
1588 * Since this entry doesn't need to be sorted
1589 * or further processed, print it right away.
1592 pem(&ep
, &ep
+ 1, 0);
1598 int sav_errno
= errno
;
1600 (void) fprintf(stderr
,
1601 gettext("ls: error reading directory %s: %s\n"),
1602 dir
, strerror(sav_errno
));
1604 (void) closedir(dirf
);
1605 colwidth
= fixedwidth
+ filewidth
;
1610 * Attaching a link to an inode's ancestors. Search
1611 * through the ancestors to check for cycles (an inode which
1612 * we have already tracked in this inodes ancestry). If a cycle
1613 * is detected, set the exit code and record the fact so that
1614 * it is reported at the right time when printing the directory.
1615 * In addition, set the exit code. Note: If the -a flag was
1616 * specified, we don't want to check for cycles for directories
1617 * ending in '/.' or '/..' unless they were specified on the
1621 record_ancestry(char *file
, struct stat
*pstatb
, struct lbuf
*rep
,
1622 int argfl
, struct ditem
*myparent
)
1625 struct ditem
*myinfo
;
1628 file_len
= strlen(file
);
1629 if (!aflg
|| argfl
|| (NOTWORKINGDIR(file
, file_len
) &&
1630 NOTPARENTDIR(file
, file_len
))) {
1632 * Add this inode's ancestry
1633 * info and insert it into the
1634 * ancestry list by pointing
1635 * back to its parent. We save
1636 * it (in rep) with the other info
1637 * we're gathering for this inode.
1639 if ((myinfo
= malloc(
1640 sizeof (struct ditem
))) == NULL
) {
1644 myinfo
->dev
= pstatb
->st_dev
;
1645 myinfo
->ino
= pstatb
->st_ino
;
1646 myinfo
->parent
= myparent
;
1647 rep
->ancinfo
= myinfo
;
1650 * If this node has the same device id and
1651 * inode number of one of its ancestors,
1652 * then we've detected a cycle.
1654 if (myparent
!= NULL
) {
1655 for (tptr
= myparent
; tptr
->parent
!= NULL
;
1656 tptr
= tptr
->parent
) {
1657 if ((tptr
->dev
== pstatb
->st_dev
) &&
1658 (tptr
->ino
== pstatb
->st_ino
)) {
1660 * Cycle detected for this
1661 * directory. Record the fact
1662 * it is a cycle so we don't
1663 * try to process this
1664 * directory as we are
1665 * walking through the
1666 * list of directories.
1678 * Do re-calculate the mode for group for ACE_T type of acls.
1679 * This is because, if the server's FS happens to be UFS, supporting
1680 * POSIX ACL's, then it does a special calculation of group mode
1681 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1683 * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1684 * algorithm is used for the group mode calculation only.
1685 * What is modified here from the algorithm is that only the
1686 * entries with flags ACE_GROUP are considered. For each entry
1687 * with ACE_GROUP flag, the first occurance of a specific access
1688 * is checked if it is allowed.
1689 * We are not interested in perms for user and other, as they
1690 * were taken from st_mode value.
1691 * We are not interested in a_who field of ACE, as we need just
1692 * unix mode bits for the group.
1695 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP)
1696 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1699 grp_mask_to_mode(struct lbuf
*p
)
1701 int mode
= 0, seen
= 0;
1705 acl_t
*acep
= p
->aclp
;
1707 acecnt
= acl_cnt(acep
);
1708 for (ap
= (ace_t
*)acl_data(acep
); acecnt
--; ap
++) {
1710 if (ap
->a_type
!= ACE_ACCESS_ALLOWED_ACE_TYPE
&&
1711 ap
->a_type
!= ACE_ACCESS_DENIED_ACE_TYPE
)
1714 if (ap
->a_flags
& ACE_INHERIT_ONLY_ACE
)
1718 * if it is first group@ or first everyone@
1719 * for each of read, write and execute, then
1720 * that will be the group mode bit.
1722 flags
= ap
->a_flags
& ACE_TYPE_FLAGS
;
1723 if (flags
== OWNED_GROUP
|| (flags
== ACE_IDENTIFIER_GROUP
&&
1724 ap
->a_who
== p
->lgid
) || flags
== ACE_EVERYONE
) {
1725 if (ap
->a_access_mask
& ACE_READ_DATA
) {
1726 if (!(seen
& S_IRGRP
)) {
1728 if (IS_TYPE_ALLOWED(ap
->a_type
))
1732 if (ap
->a_access_mask
& ACE_WRITE_DATA
) {
1733 if (!(seen
& S_IWGRP
)) {
1735 if (IS_TYPE_ALLOWED(ap
->a_type
))
1739 if (ap
->a_access_mask
& ACE_EXECUTE
) {
1740 if (!(seen
& S_IXGRP
)) {
1742 if (IS_TYPE_ALLOWED(ap
->a_type
))
1752 * get status of file and recomputes tblocks;
1753 * argfl = 1 if file is a name in ls-command and = 0
1754 * for filename in a directory whose name is an
1755 * argument in the command;
1756 * stores a pointer in flist[nfiles] and
1757 * returns that pointer;
1758 * returns NULL if failed;
1760 static struct lbuf
*
1761 gstat(char *file
, int argfl
, struct ditem
*myparent
)
1763 struct stat statb
, statb1
;
1767 int (*statf
)() = ((Lflg
) || (Hflg
&& argfl
)) ? stat
: lstat
;
1771 o_mode_t groupperm
, mask
;
1772 int grouppermfound
, maskfound
;
1777 if (nfiles
>= maxfils
) {
1779 * all flist/lbuf pair assigned files, time to get some
1783 if (((flist
= reallocarray(flist
, maxfils
,
1784 sizeof (struct lbuf
*))) == NULL
) ||
1785 ((nxtlbf
= malloc(quantn
*
1786 sizeof (struct lbuf
))) == NULL
)) {
1794 * nfiles is reset to nargs for each directory
1795 * that is given as an argument maxn is checked
1796 * to prevent the assignment of an lbuf to a flist entry
1797 * that already has one assigned.
1799 if (nfiles
>= maxn
) {
1801 flist
[nfiles
++] = rep
;
1804 rep
= flist
[nfiles
++];
1807 /* Clear the lbuf */
1808 (void) memset(rep
, 0, sizeof (struct lbuf
));
1811 * When noflist is set, none of the extra information about the dirent
1812 * will be printed, so omit remaining initialization of this lbuf
1813 * as well as the stat(2) call.
1815 if (!argfl
&& noflist
)
1818 /* Initialize non-zero members */
1820 rep
->lat
.tv_sec
= time(NULL
);
1821 rep
->lct
.tv_sec
= time(NULL
);
1822 rep
->lmt
.tv_sec
= time(NULL
);
1824 if (argfl
|| statreq
) {
1832 if ((*statf
)(file
, &statb
) < 0) {
1833 if (argfl
|| errno
!= ENOENT
||
1834 (Lflg
&& lstat(file
, &statb
) == 0)) {
1836 * Avoid race between readdir and lstat.
1837 * Print error message in case of dangling link.
1847 * If -H was specified, and the file linked to was
1848 * not a directory, then we need to get the info
1849 * for the symlink itself.
1851 if ((Hflg
) && (argfl
) &&
1852 ((statb
.st_mode
& S_IFMT
) != S_IFDIR
)) {
1853 if (lstat(file
, &statb
) < 0) {
1859 rep
->lnum
= statb
.st_ino
;
1860 rep
->lsize
= statb
.st_size
;
1861 rep
->lblocks
= statb
.st_blocks
;
1863 rep
->color
= ls_color_find(file
, statb
.st_mode
);
1865 switch (statb
.st_mode
& S_IFMT
) {
1869 record_ancestry(file
, &statb
, rep
,
1875 rep
->lsize
= (off_t
)statb
.st_rdev
;
1879 rep
->lsize
= (off_t
)statb
.st_rdev
;
1889 /* symbolic links may not have ACLs, so elide acl() */
1890 if ((Lflg
== 0) || (Hflg
== 0) ||
1891 ((Hflg
) && (!argfl
))) {
1895 if (lflg
|| colorflg
) {
1896 cc
= readlink(file
, buf
, BUFSIZ
);
1901 * follow the symbolic link
1902 * to generate the appropriate
1903 * Fflg marker for the object
1904 * eg, /bin -> /sym/bin/
1907 if (Fflg
|| pflg
|| colorflg
)
1908 error
= stat(file
, &statb1
);
1920 if ((Fflg
|| pflg
) && error
>= 0) {
1921 switch (statb1
.st_mode
& S_IFMT
) {
1935 if ((statb1
.st_mode
& ~S_IFMT
) &
1936 (S_IXUSR
|S_IXGRP
| S_IXOTH
))
1942 rep
->flinkto
= strdup(buf
);
1943 if (rep
->flinkto
== NULL
) {
1952 * ls /sym behaves differently from ls /sym/
1953 * when /sym is a symbolic link. This is fixed
1954 * when explicit arguments are specified.
1958 /* Do not follow a symlink when -F is specified */
1959 if ((!argfl
) || (argfl
&& Fflg
) ||
1960 (stat(file
, &statb1
) < 0))
1962 /* Follow a symlink when -F is specified */
1963 if (!argfl
|| stat(file
, &statb1
) < 0)
1966 if ((statb1
.st_mode
& S_IFMT
) == S_IFDIR
) {
1969 rep
->lsize
= statb1
.st_size
;
1971 record_ancestry(file
, &statb
, rep
,
1989 rep
->lflags
= statb
.st_mode
& ~S_IFMT
;
1991 if (!S_ISREG(statb
.st_mode
))
1992 rep
->lflags
|= LS_NOTREG
;
1994 rep
->luid
= statb
.st_uid
;
1995 rep
->lgid
= statb
.st_gid
;
1996 rep
->lnl
= statb
.st_nlink
;
1997 if (uflg
|| (tmflg
&& atm
))
1998 rep
->lmtime
= statb
.st_atim
;
1999 else if (cflg
|| (tmflg
&& ctm
))
2000 rep
->lmtime
= statb
.st_ctim
;
2002 rep
->lmtime
= statb
.st_mtim
;
2003 rep
->lat
= statb
.st_atim
;
2004 rep
->lct
= statb
.st_ctim
;
2005 rep
->lmt
= statb
.st_mtim
;
2007 /* ACL: check acl entries count */
2010 error
= acl_get(file
, 0, &rep
->aclp
);
2012 (void) fprintf(stderr
,
2013 gettext("ls: can't read ACL on %s: %s\n"),
2014 file
, acl_strerror(error
));
2023 ((acl_flags(rep
->aclp
) & ACL_IS_TRIVIAL
) == 0)) {
2026 * Special handling for ufs aka aclent_t ACL's
2028 if (acl_type(rep
->aclp
) == ACLENT_T
) {
2030 * For files with non-trivial acls, the
2031 * effective group permissions are the
2032 * intersection of the GROUP_OBJ value
2033 * and the CLASS_OBJ (acl mask) value.
2034 * Determine both the GROUP_OBJ and
2035 * CLASS_OBJ for this file and insert
2036 * the logical AND of those two values
2037 * in the group permissions field
2038 * of the lflags value for this file.
2042 * Until found in acl list, assume
2043 * maximum permissions for both group
2044 * a nd mask. (Just in case the acl
2045 * lacks either value for some reason.)
2051 aclcnt
= acl_cnt(rep
->aclp
);
2053 (aclent_t
*)acl_data(rep
->aclp
);
2055 if (tp
->a_type
== GROUP_OBJ
) {
2056 groupperm
= tp
->a_perm
;
2060 if (tp
->a_type
== CLASS_OBJ
) {
2064 if (grouppermfound
&& maskfound
)
2069 /* reset all the group bits */
2070 rep
->lflags
&= ~S_IRWXG
;
2073 * Now set them to the logical AND of
2074 * the GROUP_OBJ permissions and the
2078 rep
->lflags
|= (groupperm
& mask
) << 3;
2080 } else if (acl_type(rep
->aclp
) == ACE_T
) {
2082 mode
= grp_mask_to_mode(rep
);
2083 rep
->lflags
&= ~S_IRWXG
;
2084 rep
->lflags
|= mode
;
2088 if (!vflg
&& !Vflg
&& rep
->aclp
) {
2089 acl_free(rep
->aclp
);
2093 if (atflg
&& pathconf(file
, _PC_XATTR_EXISTS
) == 1)
2099 /* mask ISARG and other file-type bits */
2101 if (rep
->ltype
!= 'b' && rep
->ltype
!= 'c')
2102 tblocks
+= rep
->lblocks
;
2104 /* Get extended system attributes */
2106 if ((saflg
|| (tmflg
&& crtm
) || (tmflg
&& alltm
)) &&
2107 (sysattr_support(file
, _PC_SATTR_EXISTS
) == 1)) {
2110 sacnt
= attr_count();
2112 * Allocate 'sacnt' size array to hold extended
2113 * system attribute name (verbose) or respective
2114 * symbol represenation (compact).
2116 rep
->exttr
= xmalloc(sacnt
* sizeof (struct attrb
),
2119 /* initialize boolean attribute list */
2120 for (i
= 0; i
< sacnt
; i
++)
2121 rep
->exttr
[i
].name
= NULL
;
2122 if (get_sysxattr(file
, rep
) != 0) {
2123 (void) fprintf(stderr
,
2124 gettext("ls:Failed to retrieve "
2125 "extended system attribute from "
2127 rep
->exttr
[0].name
= xmalloc(2, rep
);
2128 (void) strlcpy(rep
->exttr
[0].name
, "?", 2);
2136 * returns pathname of the form dir/file;
2137 * dir and file are null-terminated strings.
2140 makename(char *dir
, char *file
)
2143 * PATH_MAX is the maximum length of a path name.
2144 * MAXNAMLEN is the maximum length of any path name component.
2145 * Allocate space for both, plus the '/' in the middle
2146 * and the null character at the end.
2147 * dfile is static as this is returned by makename().
2149 static char dfile
[PATH_MAX
+ 1 + MAXNAMLEN
+ 1];
2156 if (dp
> dfile
&& *(dp
- 1) != '/')
2172 #define NMAX (sizeof (utmp.ut_name))
2173 #define SCPYN(a, b) (void) strncpy(a, b, NMAX)
2176 struct cachenode
{ /* this struct must be zeroed before using */
2177 struct cachenode
*lesschild
; /* subtree whose entries < val */
2178 struct cachenode
*grtrchild
; /* subtree whose entries > val */
2179 long val
; /* the uid or gid of this entry */
2180 int initted
; /* name has been filled in */
2181 char name
[NMAX
+1]; /* the string that val maps to */
2183 static struct cachenode
*names
, *groups
;
2185 static struct cachenode
*
2186 findincache(struct cachenode
**head
, long val
)
2188 struct cachenode
**parent
= head
;
2189 struct cachenode
*c
= *parent
;
2192 if (val
== c
->val
) {
2195 } else if (val
< c
->val
) {
2196 parent
= &c
->lesschild
;
2199 parent
= &c
->grtrchild
;
2204 /* not in the cache, make a new entry for it */
2205 c
= calloc(1, sizeof (struct cachenode
));
2216 * get name from cache, or passwd file for a given uid;
2217 * lastuid is set to uid.
2222 struct passwd
*pwent
;
2223 struct cachenode
*c
;
2225 if ((uid
== lastuid
) && lastuname
)
2228 c
= findincache(&names
, uid
);
2229 if (c
->initted
== 0) {
2230 if ((pwent
= getpwuid(uid
)) != NULL
) {
2231 SCPYN(&c
->name
[0], pwent
->pw_name
);
2233 (void) sprintf(&c
->name
[0], "%-8u", (int)uid
);
2238 lastuname
= &c
->name
[0];
2243 * get name from cache, or group file for a given gid;
2244 * lastgid is set to gid.
2249 struct group
*grent
;
2250 struct cachenode
*c
;
2252 if ((gid
== lastgid
) && lastgname
)
2255 c
= findincache(&groups
, gid
);
2256 if (c
->initted
== 0) {
2257 if ((grent
= getgrgid(gid
)) != NULL
) {
2258 SCPYN(&c
->name
[0], grent
->gr_name
);
2260 (void) sprintf(&c
->name
[0], "%-8u", (int)gid
);
2265 lastgname
= &c
->name
[0];
2269 /* return >0 if item pointed by pp2 should appear first */
2271 compar(struct lbuf
**pp1
, struct lbuf
**pp2
)
2273 struct lbuf
*p1
, *p2
;
2279 * compare two names in ls-command one of which is file
2280 * and the other is a directory;
2281 * this portion is not used for comparing files within
2282 * a directory name of ls-command;
2284 if (p1
->lflags
&ISARG
&& p1
->ltype
== 'd') {
2285 if (!(p2
->lflags
&ISARG
&& p2
->ltype
== 'd'))
2288 if (p2
->lflags
&ISARG
&& p2
->ltype
== 'd')
2293 if (p2
->lmtime
.tv_sec
> p1
->lmtime
.tv_sec
)
2295 else if (p2
->lmtime
.tv_sec
< p1
->lmtime
.tv_sec
)
2297 /* times are equal to the sec, check nsec */
2298 if (p2
->lmtime
.tv_nsec
> p1
->lmtime
.tv_nsec
)
2300 else if (p2
->lmtime
.tv_nsec
< p1
->lmtime
.tv_nsec
)
2302 /* if times are equal, fall through and sort by name */
2305 * The size stored in lsize can be either the
2306 * size or the major minor number (in the case of
2307 * block and character special devices). If it's
2308 * a major minor number, then the size is considered
2309 * to be zero and we want to fall through and sort
2310 * by name. In addition, if the size of p2 is equal
2311 * to the size of p1 we want to fall through and
2314 off_t p1size
= (p1
->ltype
== 'b') ||
2315 (p1
->ltype
== 'c') ? 0 : p1
->lsize
;
2316 off_t p2size
= (p2
->ltype
== 'b') ||
2317 (p2
->ltype
== 'c') ? 0 : p2
->lsize
;
2318 if (p2size
> p1size
) {
2320 } else if (p2size
< p1size
) {
2323 /* Sizes are equal, fall through and sort by name. */
2325 return (rflg
* strcoll(
2326 p1
->lflags
& ISARG
? p1
->ln
.namep
: p1
->ln
.lname
,
2327 p2
->lflags
&ISARG
? p2
->ln
.namep
: p2
->ln
.lname
));
2331 pprintf(char *s1
, char *s2
)
2333 csi_pprintf((unsigned char *)s1
);
2334 csi_pprintf((unsigned char *)s2
);
2338 csi_pprintf(unsigned char *s
)
2347 if (!qflg
&& !bflg
) {
2348 for (cp
= s
; *cp
!= '\0'; cp
++) {
2349 (void) putchar(*cp
);
2355 for (cp
= s
; *cp
; ) {
2356 if (isascii(c
= *cp
)) {
2362 (void) putc('\\', stdout
);
2363 c
= '0' + ((*cp
>> 6) & 07);
2364 (void) putc(c
, stdout
);
2365 c
= '0' + ((*cp
>> 3) & 07);
2366 (void) putc(c
, stdout
);
2367 c
= '0' + (*cp
& 07);
2372 (void) putc(c
, stdout
);
2376 if ((c_len
= mbtowc(&pcode
, (char *)cp
, MB_LEN_MAX
)) <= 0) {
2381 if ((p_col
= wcwidth(pcode
)) > 0) {
2382 (void) putwchar(pcode
);
2389 for (i
= 0; i
< c_len
; i
++) {
2394 (void) putc('\\', stdout
);
2395 c
= '0' + ((*cp
>> 6) & 07);
2396 (void) putc(c
, stdout
);
2397 c
= '0' + ((*cp
>> 3) & 07);
2398 (void) putc(c
, stdout
);
2399 c
= '0' + (*cp
& 07);
2402 (void) putc(c
, stdout
);
2409 strcol(unsigned char *s1
)
2424 if ((len
= mbtowc(&wc
, (char *)s1
, MB_LEN_MAX
)) <= 0) {
2430 if ((w_col
= wcwidth(wc
)) < 0)
2438 /* Get extended system attributes and set the display */
2441 get_sysxattr(char *fname
, struct lbuf
*rep
)
2449 if ((error
= getattrat(AT_FDCWD
, XATTR_VIEW_READWRITE
, fname
,
2451 perror("ls:getattrat");
2456 * Allocate 'sacnt' size array to hold extended timestamp
2457 * system attributes and initialize the array.
2459 rep
->extm
= xmalloc(sacnt
* sizeof (struct attrtm
), rep
);
2460 for (i
= 0; i
< sacnt
; i
++) {
2461 rep
->extm
[i
].stm
= 0;
2462 rep
->extm
[i
].nstm
= 0;
2463 rep
->extm
[i
].name
= NULL
;
2465 while ((pair
= nvlist_next_nvpair(response
, pair
)) != NULL
) {
2466 name
= nvpair_name(pair
);
2467 type
= nvpair_type(pair
);
2468 if (type
== DATA_TYPE_BOOLEAN_VALUE
) {
2469 error
= nvpair_value_boolean_value(pair
, &value
);
2471 (void) fprintf(stderr
,
2472 gettext("nvpair_value_boolean_value "
2473 "failed: error = %d\n"), error
);
2477 set_sysattrb_display(name
, value
, rep
);
2479 } else if (type
== DATA_TYPE_UINT64_ARRAY
) {
2481 set_sysattrtm_display(name
, rep
);
2485 nvlist_free(response
);
2489 /* Set extended system attribute boolean display */
2492 set_sysattrb_display(char *name
, boolean_t val
, struct lbuf
*rep
)
2498 fattr
= name_to_attr(name
);
2499 if (fattr
!= F_ATTR_INVAL
&& fattr
< sacnt
) {
2503 rep
->exttr
[fattr
].name
= xmalloc(len
+ 1, rep
);
2504 (void) strlcpy(rep
->exttr
[fattr
].name
, name
,
2507 rep
->exttr
[fattr
].name
= xmalloc(len
+ 3, rep
);
2508 (void) snprintf(rep
->exttr
[fattr
].name
, len
+ 3,
2512 opt
= attr_to_option(fattr
);
2515 rep
->exttr
[fattr
].name
= xmalloc(len
+ 1, rep
);
2517 (void) strlcpy(rep
->exttr
[fattr
].name
,
2520 (void) strlcpy(rep
->exttr
[fattr
].name
,
2527 /* Set extended system attribute timestamp display */
2530 set_sysattrtm_display(char *name
, struct lbuf
*rep
)
2537 if (nvpair_value_uint64_array(pair
, &value
, &nelem
) == 0) {
2538 if (*value
!= (uintptr_t)NULL
) {
2541 while (rep
->extm
[i
].stm
!= 0 && i
< sacnt
)
2543 rep
->extm
[i
].stm
= value
[0];
2544 rep
->extm
[i
].nstm
= value
[1];
2545 rep
->extm
[i
].name
= xmalloc(len
+ 1, rep
);
2546 (void) strlcpy(rep
->extm
[i
].name
, name
, len
+ 1);
2552 format_time(time_t sec
, time_t nsec
)
2554 const char *fstr
= time_fmt_new
;
2555 char fmt_buf
[FMTSIZE
];
2558 (void) snprintf(fmt_buf
, FMTSIZE
, fstr
, nsec
);
2559 (void) strftime(time_buf
, sizeof (time_buf
), fmt_buf
,
2564 if (sec
< year
|| sec
> now
)
2565 fstr
= time_fmt_old
;
2567 /* if a custom time was specified, shouldn't be localized */
2568 (void) strftime(time_buf
, sizeof (time_buf
),
2569 (time_custom
== 0) ? dcgettext(NULL
, fstr
, LC_TIME
) : fstr
,
2574 format_attrtime(struct lbuf
*p
)
2579 if (p
->extm
!= NULL
) {
2580 for (i
= 0; i
< sacnt
; i
++) {
2581 if (p
->extm
[i
].name
!= NULL
) {
2589 const char *old_save
= time_fmt_old
;
2590 const char *new_save
= time_fmt_new
;
2592 /* Eflg always sets format to FORMAT_ISO_FULL */
2593 if (!Eflg
&& !time_custom
) {
2594 time_fmt_old
= FORMAT_OLD
;
2595 time_fmt_new
= FORMAT_NEW
;
2598 format_time((time_t)p
->extm
[i
].stm
, (time_t)p
->extm
[i
].nstm
);
2600 time_fmt_old
= old_save
;
2601 time_fmt_new
= new_save
;
2606 print_time(struct lbuf
*p
)
2608 const char *old_save
= time_fmt_old
;
2609 const char *new_save
= time_fmt_new
;
2614 time_fmt_old
= FORMAT_LONG
;
2615 time_fmt_new
= FORMAT_LONG
;
2619 format_time(p
->lat
.tv_sec
, p
->lat
.tv_nsec
);
2620 (void) printf(" timestamp: atime %s\n", time_buf
);
2621 format_time(p
->lct
.tv_sec
, p
->lct
.tv_nsec
);
2622 (void) printf(" timestamp: ctime %s\n", time_buf
);
2623 format_time(p
->lmt
.tv_sec
, p
->lmt
.tv_nsec
);
2624 (void) printf(" timestamp: mtime %s\n", time_buf
);
2625 if (p
->extm
!= NULL
) {
2626 while (p
->extm
[i
].nstm
!= 0 && i
< sacnt
) {
2627 format_time(p
->extm
[i
].stm
, p
->extm
[i
].nstm
);
2628 if (p
->extm
[i
].name
!= NULL
) {
2629 (void) printf(" timestamp:"
2631 p
->extm
[i
].name
, time_buf
);
2637 time_fmt_old
= old_save
;
2638 time_fmt_new
= new_save
;
2642 * Check if color definition applies to entry, returns 1 if yes, 0 if no
2645 color_match(const char *fname
, mode_t mode
, ls_color_t
*color
)
2647 switch (color
->ftype
) {
2650 size_t fname_len
, sfx_len
;
2652 fname_len
= strlen(fname
);
2653 sfx_len
= strlen(color
->sfx
);
2654 if (sfx_len
> fname_len
)
2657 if (strcmp(color
->sfx
, fname
+ fname_len
- sfx_len
) == 0)
2667 return (S_ISREG(mode
));
2670 return (S_ISDIR(mode
));
2673 return (S_ISLNK(mode
));
2676 return (S_ISFIFO(mode
));
2679 return (S_ISSOCK(mode
));
2682 return (S_ISDOOR(mode
));
2685 return (S_ISBLK(mode
));
2688 return (S_ISCHR(mode
));
2691 return (S_ISPORT(mode
));
2694 /* this is tested for by gstat */
2698 return (!S_ISLNK(mode
) && (mode
& S_ISUID
));
2701 return (!S_ISLNK(mode
) && (mode
& S_ISGID
));
2703 case LS_STICKY_OTHER_WRITABLE
:
2704 return (!S_ISLNK(mode
) && (mode
& (S_IWOTH
|S_ISVTX
)));
2706 case LS_OTHER_WRITABLE
:
2707 return (!S_ISLNK(mode
) && (mode
& S_IWOTH
));
2710 return (!S_ISLNK(mode
) && (mode
& S_ISVTX
));
2713 return (!S_ISLNK(mode
) && (mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)));
2720 dump_color(ls_color_t
*c
)
2725 (void) printf("\n\ttype: ");
2728 (void) printf("LS_NORMAL");
2731 (void) printf("LS_FILE");
2734 (void) printf("LS_EXEC");
2737 (void) printf("LS_DIR");
2740 (void) printf("LS_LINK");
2744 (void) printf("LS_FIFO");
2748 (void) printf("LS_SOCK");
2752 (void) printf("LS_DOOR");
2756 (void) printf("LS_BLK");
2760 (void) printf("LS_CHR");
2764 (void) printf("LS_PORT");
2768 (void) printf("LS_STICKY");
2772 (void) printf("LS_ORPHAN");
2776 (void) printf("LS_SETGID");
2780 (void) printf("LS_SETUID");
2783 case LS_OTHER_WRITABLE
:
2784 (void) printf("LS_OTHER_WRITABLE");
2787 case LS_STICKY_OTHER_WRITABLE
:
2788 (void) printf("LS_STICKY_OTHER_WRITABLE");
2792 (void) printf("LS_PAT\n");
2793 (void) printf("\tpattern: %s", c
->sfx
);
2796 (void) printf("\n");
2797 (void) printf("\tattr: %d\n", c
->attr
);
2798 (void) printf("\tfg: %d\n", c
->fg
);
2799 (void) printf("\tbg: %d\n", c
->bg
);
2800 (void) printf("\t");
2804 ls_color_find(const char *fname
, mode_t mode
)
2809 * Colors are sorted from most general lsc_colors[0] to most specific
2810 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with
2811 * most specific color rule and work towards most general.
2813 for (i
= lsc_ncolors
- 1; i
>= 0; --i
)
2814 if (color_match(fname
, mode
, &lsc_colors
[i
]))
2815 return (&lsc_colors
[i
]);
2821 ls_tprint(char *str
, long int p1
, long int p2
, long int p3
, long int p4
,
2822 long int p5
, long int p6
, long int p7
, long int p8
, long int p9
)
2829 s
= tparm(str
, p1
, p2
, p3
, p4
, p5
, p6
, p7
, p8
, p9
);
2836 ls_start_color(ls_color_t
*c
)
2844 if (c
->attr
& LSA_BOLD
)
2845 ls_tprint(lsc_bold
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2846 if (c
->attr
& LSA_UNDERSCORE
)
2847 ls_tprint(lsc_underline
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2848 if (c
->attr
& LSA_BLINK
)
2849 ls_tprint(lsc_blink
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2850 if (c
->attr
& LSA_REVERSE
)
2851 ls_tprint(lsc_reverse
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2852 if (c
->attr
& LSA_CONCEALED
)
2853 ls_tprint(lsc_concealed
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2854 if (c
->attr
== LSA_NONE
)
2855 ls_tprint(lsc_none
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2858 ls_tprint(lsc_setfg
, c
->fg
, 0, 0, 0, 0, 0, 0, 0, 0);
2860 ls_tprint(lsc_setbg
, c
->bg
, 0, 0, 0, 0, 0, 0, 0, 0);
2866 ls_tprint(lsc_none
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2868 dump_color(lsc_match
);
2872 new_color_entry(char *colorstr
)
2874 static const struct {
2878 { "no", LS_NORMAL
},
2887 { "or", LS_ORPHAN
},
2888 { "su", LS_SETUID
},
2889 { "sg", LS_SETGID
},
2890 { "tw", LS_STICKY_OTHER_WRITABLE
},
2891 { "ow", LS_OTHER_WRITABLE
},
2892 { "st", LS_STICKY
},
2902 p
= strtok_r(colorstr
, "=", &lasts
);
2909 lsc_colors
[lsc_ncolors
].ftype
= LS_PAT
;
2910 /* don't include the * in the suffix */
2911 if ((lsc_colors
[lsc_ncolors
].sfx
= strdup(p
+ 1)) == NULL
) {
2916 lsc_colors
[lsc_ncolors
].sfx
= NULL
;
2918 for (i
= 0; type_map
[i
].s
!= NULL
; ++i
) {
2919 if (strncmp(type_map
[i
].s
, p
, 2) == 0)
2923 /* ignore unknown file types */
2924 if (type_map
[i
].s
== NULL
)
2927 lsc_colors
[lsc_ncolors
].ftype
= type_map
[i
].stype
;
2931 lsc_colors
[lsc_ncolors
].fg
= -1;
2932 lsc_colors
[lsc_ncolors
].bg
= -1;
2933 for (p
= strtok_r(NULL
, ";", &lasts
); p
!= NULL
;
2934 p
= strtok_r(NULL
, ";", &lasts
)) {
2935 color
= strtol(p
, NULL
, 10);
2946 attr
|= LSA_UNDERSCORE
;
2952 attr
|= LSA_REVERSE
;
2955 attr
|= LSA_CONCEALED
;
2963 lsc_colors
[lsc_ncolors
].fg
= color
- 30;
2965 lsc_colors
[lsc_ncolors
].bg
= color
- 40;
2968 lsc_colors
[lsc_ncolors
].attr
= attr
;
2973 ls_color_compare(const void *p1
, const void *p2
)
2975 const ls_color_t
*c1
= (const ls_color_t
*)p1
;
2976 const ls_color_t
*c2
= (const ls_color_t
*)p2
;
2978 int ret
= c1
->ftype
- c2
->ftype
;
2983 if (c1
->ftype
!= LS_PAT
)
2986 return (strcmp(c1
->sfx
, c2
->sfx
));
2992 static char *default_colorstr
= "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
2993 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
2994 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
2995 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
2996 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
2997 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
2998 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
2999 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3000 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3001 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3002 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3010 (void) setupterm(NULL
, 1, &termret
);
3014 if ((p
= getenv("LS_COLORS")) == NULL
)
3015 p
= default_colorstr
;
3016 colorstr
= strdup(p
);
3017 if (colorstr
== NULL
)
3021 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors
3022 * if there are invalid entries passed in the string (they are ignored)
3025 for (p
= strchr(colorstr
, ':'); p
!= NULL
&& *p
!= '\0';
3026 p
= strchr(++p
, ':'))
3029 lsc_colors
= calloc(color_sz
, sizeof (ls_color_t
));
3030 if (lsc_colors
== NULL
) {
3035 for (p
= strtok_r(colorstr
, ":", &lasts
);
3036 p
!= NULL
&& lsc_ncolors
< color_sz
;
3037 p
= strtok_r(NULL
, ":", &lasts
))
3040 qsort((void *)lsc_colors
, lsc_ncolors
, sizeof (ls_color_t
),
3043 for (i
= 0; i
< lsc_ncolors
; ++i
)
3044 if (lsc_colors
[i
].ftype
== LS_ORPHAN
) {
3045 lsc_orphan
= &lsc_colors
[i
];
3049 if ((lsc_bold
= tigetstr("bold")) == (char *)-1)
3052 if ((lsc_underline
= tigetstr("smul")) == (char *)-1)
3053 lsc_underline
= NULL
;
3055 if ((lsc_blink
= tigetstr("blink")) == (char *)-1)
3058 if ((lsc_reverse
= tigetstr("rev")) == (char *)-1)
3061 if ((lsc_concealed
= tigetstr("prot")) == (char *)-1)
3062 lsc_concealed
= NULL
;
3064 if ((lsc_none
= tigetstr("sgr0")) == (char *)-1)
3067 if ((lsc_setfg
= tigetstr("setaf")) == (char *)-1)
3070 if ((lsc_setbg
= tigetstr("setab")) == (char *)-1)
3073 if (getenv("_LS_COLOR_DEBUG") != NULL
) {
3077 for (i
= 0; i
< lsc_ncolors
; ++i
)
3078 dump_color(&lsc_colors
[i
]);
3084 /* Free extended system attribute lists */
3087 free_sysattr(struct lbuf
*p
)
3091 if (p
->exttr
!= NULL
) {
3092 for (i
= 0; i
< sacnt
; i
++) {
3093 free(p
->exttr
[i
].name
);
3097 if (p
->extm
!= NULL
) {
3098 for (i
= 0; i
< sacnt
; i
++) {
3099 free(p
->extm
[i
].name
);
3105 /* Allocate extended system attribute list */
3108 xmalloc(size_t size
, struct lbuf
*p
)
3110 if ((p
= malloc(size
)) == NULL
) {
3113 nvlist_free(response
);