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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright 2009 Jason King. All rights reserved.
29 * Use is subject to license terms.
32 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
33 /* All Rights Reserved */
35 /* Copyright (c) 1987, 1988 Microsoft Corporation */
36 /* All Rights Reserved */
39 * List files or directories
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/mkdev.h>
69 #include <libnvpair.h>
70 #include <libcmdutils.h>
80 * -DNOTERMINFO can be defined on the cc command line to prevent
81 * the use of terminfo. This should be done on systems not having
82 * the terminfo feature(pre 6.0 systems ?).
83 * As a result, columnar listings assume 80 columns for output,
84 * unless told otherwise via the COLUMNS environment variable.
93 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
97 * this flag has been added to manipulate the display of S instead of 'l' when
98 * the file is not a regular file and when group execution bit is off
100 #define LS_NOTREG 010000
104 * Date and time formats
106 * b --- abbreviated month name
108 * Y --- year in the form ccyy
109 * H --- hour(24-hour version)
113 * z --- time zone as hours displacement from UTC
114 * note that %F and %z are from the ISO C99 standard and are
115 * not present in older C libraries
117 #define FORMAT_OLD " %b %e %Y "
118 #define FORMAT_NEW " %b %e %H:%M "
119 #define FORMAT_LONG " %b %e %T %Y "
120 #define FORMAT_ISO_FULL " %%F %%T.%.09ld %%z "
121 #define FORMAT_ISO_LONG " %F %R "
122 #define FORMAT_ISO_NEW " %m-%d %H:%M "
123 #define FORMAT_ISO_OLD " %F "
127 #define NUMBER_WIDTH 40
131 dev_t dev
; /* directory items device number */
132 ino_t ino
; /* directory items inode number */
133 struct ditem
*parent
; /* dir items ptr to its parent's info */
135 /* Holds boolean extended system attributes */
139 /* Holds timestamp extended system attributes */
147 #define LSA_BOLD (1L << 0)
148 #define LSA_UNDERSCORE (1L << 1)
149 #define LSA_BLINK (1L << 2)
150 #define LSA_REVERSE (1L << 3)
151 #define LSA_CONCEALED (1L << 4)
153 /* these should be ordered most general to most specific */
154 typedef enum LS_CFTYPE
{
171 LS_STICKY_OTHER_WRITABLE
,
185 char lname
[MAXNAMLEN
]; /* used for filename in a directory */
186 char *namep
; /* for name in ls-command; */
188 char ltype
; /* filetype */
189 ino_t lnum
; /* inode number of file */
190 mode_t lflags
; /* 0777 bits used as r,w,x permissions */
191 nlink_t lnl
; /* number of links to file */
194 off_t lsize
; /* filesize or major/minor dev numbers */
195 blkcnt_t lblocks
; /* number of file blocks */
200 char *flinkto
; /* symbolic link contents */
201 char acl
; /* indicate there are additional acl entries */
202 int cycle
; /* cycle detected flag */
203 struct ditem
*ancinfo
; /* maintains ancestor info */
204 acl_t
*aclp
; /* ACL if present */
205 struct attrb
*exttr
; /* boolean extended system attributes */
206 struct attrtm
*extm
; /* timestamp extended system attributes */
207 ls_color_t
*color
; /* color for entry */
208 ls_color_t
*link_color
; /* color for symlink */
212 char *dc_name
; /* path name */
213 int cycle_detected
; /* cycle detected visiting this directory */
214 struct ditem
*myancinfo
; /* this directory's ancestry info */
215 struct dchain
*dc_next
; /* next directory in the chain */
219 * A numbuf_t is used when converting a number to a string representation
221 typedef char numbuf_t
[NUMBER_WIDTH
];
223 static struct dchain
*dfirst
; /* start of the dir chain */
224 static struct dchain
*cdfirst
; /* start of the current dir chain */
225 static struct dchain
*dtemp
; /* temporary - used for linking */
226 static char *curdir
; /* the current directory */
228 static int first
= 1; /* true if first line is not yet printed */
229 static int nfiles
= 0; /* number of flist entries in current use */
230 static int nargs
= 0; /* number of flist entries used for arguments */
231 static int maxfils
= 0; /* number of flist/lbuf entries allocated */
232 static int maxn
= 0; /* number of flist entries with lbufs asigned */
233 static int quantn
= 64; /* allocation growth quantum */
235 static struct lbuf
*nxtlbf
; /* ptr to next lbuf to be assigned */
236 static struct lbuf
**flist
; /* ptr to list of lbuf pointers */
237 static struct lbuf
*gstat(char *, int, struct ditem
*);
238 static char *getname(uid_t
);
239 static char *getgroup(gid_t
);
240 static char *makename(char *, char *);
241 static void pentry(struct lbuf
*);
242 static void column(void);
243 static void pmode(mode_t aflag
);
244 static void selection(int *);
245 static void new_line(void);
246 static void rddir(char *, struct ditem
*);
247 static int strcol(unsigned char *);
248 static void pem(struct lbuf
**, struct lbuf
**, int);
249 static void pdirectory(char *, int, int, int, struct ditem
*);
250 static struct cachenode
*findincache(struct cachenode
**, long);
251 static void csi_pprintf(unsigned char *);
252 static void pprintf(char *, char *);
253 static int compar(struct lbuf
**pp1
, struct lbuf
**pp2
);
254 static char *number_to_scaled_string(numbuf_t buf
,
255 unsigned long long number
,
257 static void record_ancestry(char *, struct stat
*, struct lbuf
*,
258 int, struct ditem
*);
259 static void ls_color_init(void);
260 static ls_color_t
*ls_color_find(const char *, mode_t
);
261 static void ls_start_color(ls_color_t
*);
262 static void ls_end_color(void);
280 static int rflg
= 1; /* init to 1 for special use in compar */
298 static int saflg
; /* boolean extended system attr. */
299 static int sacnt
; /* number of extended system attr. */
302 static int tmflg
; /* create time ext. system attr. */
310 static int err
= 0; /* Contains return code */
312 static int file_typeflg
;
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
;
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) &&
465 if (strcmp(optarg
, "never") == 0 ||
466 strcmp(optarg
, "no") == 0 ||
467 strcmp(optarg
, "none") == 0) {
471 (void) fprintf(stderr
,
472 gettext("Invalid argument '%s' for "
473 "--color\n"), optarg
);
478 if (strcmp(long_options
[option_index
].name
,
485 if (strcmp(long_options
[option_index
].name
,
486 "block-size") == 0) {
487 size_t scale_len
= strlen(optarg
);
489 uint64_t kilo
= 1024;
492 if (scale_len
== 0) {
493 (void) fprintf(stderr
, gettext(
494 "Invalid block size \'%s\'\n"),
499 scale_c
= optarg
[scale_len
- 1];
500 if (scale_c
== 'B') {
501 /* need at least digit, scale, B */
503 (void) fprintf(stderr
, gettext(
504 "Invalid block size "
505 "\'%s\'\n"), optarg
);
509 scale_c
= optarg
[scale_len
- 2];
510 if (isdigit(scale_c
)) {
511 (void) fprintf(stderr
,
512 gettext("Invalid block size"
513 " \'%s\'\n"), optarg
);
517 * make optarg[scale_len - 1] point to
557 if (!isdigit(scale_c
)) {
558 (void) fprintf(stderr
,
559 gettext("Invalid character "
560 "following block size in "
561 "\'%s\'\n"), optarg
);
566 /* NULL out scale constant if present */
567 if (scale
> 1 && !isdigit(scale_c
))
568 optarg
[scale_len
- 1] = '\0';
570 /* Based on testing, this is what GNU ls does */
571 block_size
= strtoll(optarg
, NULL
, 0) * scale
;
572 if (block_size
< 1) {
573 (void) fprintf(stderr
,
574 gettext("Invalid block size "
575 "\'%s\'\n"), optarg
);
581 if (strcmp(long_options
[option_index
].name
,
590 if (strcmp(long_options
[option_index
].name
,
595 time_fmt_old
= FORMAT_ISO_FULL
;
596 time_fmt_new
= FORMAT_ISO_FULL
;
600 if (strcmp(long_options
[option_index
].name
,
601 "time-style") == 0) {
602 /* like -E, but doesn't imply -l */
603 if (strcmp(optarg
, "full-iso") == 0) {
607 time_fmt_old
= FORMAT_ISO_FULL
;
608 time_fmt_new
= FORMAT_ISO_FULL
;
611 if (strcmp(optarg
, "long-iso") == 0) {
615 time_fmt_old
= FORMAT_ISO_LONG
;
616 time_fmt_new
= FORMAT_ISO_LONG
;
619 if (strcmp(optarg
, "iso") == 0) {
623 time_fmt_old
= FORMAT_ISO_OLD
;
624 time_fmt_new
= FORMAT_ISO_NEW
;
627 /* should be the default */
628 if (strcmp(optarg
, "locale") == 0) {
629 time_fmt_old
= FORMAT_OLD
;
630 time_fmt_new
= FORMAT_NEW
;
633 if (optarg
[0] == '+') {
636 size_t timelen
= strlen(optarg
);
638 p
= strchr(optarg
, '\n');
643 * Time format requires a leading and
645 * Add room for 3 spaces + 2 nulls
646 * The + in optarg is replaced with
650 told
= malloc(timelen
);
652 perror("Out of memory");
656 (void) memset(told
, 0, timelen
);
658 (void) strlcat(told
, &optarg
[1],
660 (void) strlcat(told
, " ", timelen
);
665 tnew
= told
+ strlen(told
) + 1;
670 (void) strlcat(tnew
, p
,
672 (void) strlcat(tnew
, " ",
681 time_fmt_old
= (const char *)told
;
726 time_fmt_old
= FORMAT_LONG
;
727 time_fmt_new
= FORMAT_LONG
;
734 time_fmt_old
= FORMAT_ISO_FULL
;
735 time_fmt_new
= FORMAT_ISO_FULL
;
755 /* -H and -L are mutually exclusive */
774 /* -H and -L are mutually exclusive */
859 num_cols
= atoi(optarg
);
875 * -l has precedence over -@
889 if (optarg
!= NULL
) {
890 if (strcmp(optarg
, "c") == 0) {
893 } else if (strcmp(optarg
, "v") == 0) {
908 if (optarg
!= NULL
) {
909 if (strcmp(optarg
, "ctime") == 0) {
914 } else if (strcmp(optarg
, "atime") == 0) {
921 } else if (strcmp(optarg
, "mtime") == 0) {
928 } else if (strcmp(optarg
, "crtime") == 0) {
935 } else if (strcmp(optarg
, "all") == 0) {
956 (void) fprintf(stderr
, gettext(
957 "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
958 "%%[atime | crtime | ctime | mtime | all]"
989 if (!wflg
&& (Cflg
|| mflg
)) {
991 if ((clptr
= getenv("COLUMNS")) != NULL
)
992 num_cols
= atoi(clptr
);
995 if (ioctl(1, TIOCGWINSZ
, &win
) != -1)
996 num_cols
= (win
.ws_col
== 0 ? 80 : win
.ws_col
);
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
;
1022 for (i
= 0; i
< (amino
? amino
: 1); i
++) {
1025 * If we are recursing, we need to make sure we don't
1026 * get into an endless loop. To keep track of the inodes
1027 * (actually, just the directories) visited, we
1028 * maintain a directory ancestry list for a file
1029 * hierarchy. As we go deeper into the hierarchy,
1030 * a parent directory passes its directory list
1031 * info (device id, inode number, and a pointer to
1032 * its parent) to each of its children. As we
1033 * process a child that is a directory, we save
1034 * its own personal directory list info. We then
1035 * check to see if the child has already been
1036 * processed by comparing its device id and inode
1037 * number from its own personal directory list info
1038 * to that of each of its ancestors. If there is a
1039 * match, then we know we've detected a cycle.
1043 * This is the first parent in this lineage
1044 * (first in a directory hierarchy), so
1045 * this parent's parent doesn't exist. We
1046 * only initialize myinfo when we are
1047 * recursing, otherwise it's not used.
1049 if ((myinfo
= (struct ditem
*)malloc(
1050 sizeof (struct ditem
))) == NULL
) {
1056 myinfo
->parent
= NULL
;
1061 width
= strcol((unsigned char *)argv
[optind
]);
1062 if (width
> filewidth
)
1065 if ((ep
= gstat((*argv
[optind
] ? argv
[optind
] : dotp
),
1066 1, myinfo
)) == NULL
) {
1073 ep
->ln
.namep
= (*argv
[optind
] ? argv
[optind
] : dotp
);
1074 ep
->lflags
|= ISARG
;
1076 nargs
++; /* count good arguments stored in flist */
1080 colwidth
= fixedwidth
+ filewidth
;
1082 qsort(flist
, (unsigned)nargs
, sizeof (struct lbuf
*),
1083 (int (*)(const void *, const void *))compar
);
1084 for (i
= 0; i
< nargs
; i
++) {
1085 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
);
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
) {
1209 ncols
= num_cols
/ colwidth
;
1213 if (ncols
== 1 || mflg
|| xflg
|| !Cflg
) {
1214 for (ep
= slp
; ep
< lp
; ep
++)
1219 /* otherwise print -C columns */
1227 nrows
= (lp
- slp
- 1) / ncols
+ 1;
1228 for (i
= 0; i
< nrows
; i
++, row
++) {
1229 for (col
= 0; col
< ncols
; col
++) {
1230 ep
= slp
+ (nrows
* col
) + row
;
1239 * print one output entry;
1240 * if uid/gid is not found in the appropriate
1241 * file(passwd/group), then print uid/gid instead of
1245 pentry(struct lbuf
*ap
)
1249 char *dmark
= ""; /* Used if -p or -F option active */
1257 curcol
+= printf("%llu ", (long long)p
->lnum
);
1259 curcol
+= printf("%10llu ", (long long)p
->lnum
);
1261 curcol
+= printf((mflg
&& !lflg
) ? "%lld " :
1262 (p
->lblocks
< 10000) ? "%4lld " : "%lld ",
1263 (p
->ltype
!= 'b' && p
->ltype
!= 'c') ?
1266 (void) putchar(p
->ltype
);
1270 /* ACL: additional access mode flag */
1271 (void) putchar(p
->acl
);
1274 curcol
+= printf("%3lu ", (ulong_t
)p
->lnl
);
1277 cp
= getname(p
->luid
);
1278 curcol
+= printf("%-8s ", cp
);
1280 curcol
+= printf("%-8lu ", (ulong_t
)p
->luid
);
1283 cp
= getgroup(p
->lgid
);
1284 curcol
+= printf("%-8s ", cp
);
1286 curcol
+= printf("%-8lu ", (ulong_t
)p
->lgid
);
1287 if (p
->ltype
== 'b' || p
->ltype
== 'c') {
1288 curcol
+= printf("%3u, %2u",
1289 (uint_t
)major((dev_t
)p
->lsize
),
1290 (uint_t
)minor((dev_t
)p
->lsize
));
1291 } else if (hflg
&& (p
->lsize
>= hscale
)) {
1292 curcol
+= printf("%7s",
1293 number_to_scaled_string(hbuf
, p
->lsize
, hscale
));
1295 uint64_t bsize
= p
->lsize
/ block_size
;
1298 * Round up only when using blocks > 1 byte, otherwise
1299 * 'normal' sizes display 1 byte too large.
1301 if (p
->lsize
% block_size
!= 0)
1304 curcol
+= printf("%7" PRIu64
, bsize
);
1306 format_time(p
->lmtime
.tv_sec
, p
->lmtime
.tv_nsec
);
1307 /* format extended system attribute time */
1311 curcol
+= printf("%s", time_buf
);
1315 * prevent both "->" and trailing marks
1319 if (pflg
&& p
->ltype
== 'd')
1322 if (Fflg
&& !(lflg
&& p
->flinkto
)) {
1323 if (p
->ltype
== 'd')
1325 else if (p
->ltype
== 'D')
1327 else if (p
->ltype
== 'p')
1329 else if (p
->ltype
== 'l')
1331 else if (p
->ltype
== 's')
1333 else if (!file_typeflg
&&
1334 (p
->lflags
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)))
1341 ls_start_color(p
->color
);
1343 if (p
->lflags
& ISARG
)
1349 csi_pprintf((unsigned char *)str
);
1351 if (lflg
&& p
->flinkto
) {
1354 csi_pprintf((unsigned char *)" -> ");
1356 ls_start_color(p
->link_color
);
1357 csi_pprintf((unsigned char *)p
->flinkto
);
1359 csi_pprintf((unsigned char *)dmark
);
1362 (void) printf("%s", str
);
1363 curcol
+= strcol((unsigned char *)str
);
1365 if (lflg
&& p
->flinkto
) {
1369 (void) printf("%s", str
);
1370 curcol
+= strcol((unsigned char *)str
);
1372 ls_start_color(p
->link_color
);
1373 (void) printf("%s", p
->flinkto
);
1374 curcol
+= strcol((unsigned char *)p
->flinkto
);
1376 (void) printf("%s", dmark
);
1377 curcol
+= strcol((unsigned char *)dmark
);
1384 /* Display extended system attributes */
1389 (void) printf(" \t{");
1390 if (p
->exttr
!= NULL
) {
1392 for (i
= 0; i
< sacnt
; i
++) {
1393 if (p
->exttr
[i
].name
!= NULL
)
1396 for (i
= 0; i
< sacnt
; i
++) {
1397 if (p
->exttr
[i
].name
!= NULL
) {
1398 (void) printf("%s", p
->exttr
[i
].name
);
1400 if (vopt
&& (k
!= 0))
1405 (void) printf("}\n");
1407 /* Display file timestamps and extended system attribute timestamps */
1408 if (tmflg
&& alltm
) {
1416 acl_printacl(p
->aclp
, num_cols
, Vflg
);
1419 /* Free extended system attribute lists */
1424 /* print various r,w,x permissions */
1428 /* these arrays are declared static to allow initializations */
1429 static int m0
[] = { 1, S_IRUSR
, 'r', '-' };
1430 static int m1
[] = { 1, S_IWUSR
, 'w', '-' };
1431 static int m2
[] = { 3, S_ISUID
|S_IXUSR
, 's', S_IXUSR
,
1432 'x', S_ISUID
, 'S', '-' };
1433 static int m3
[] = { 1, S_IRGRP
, 'r', '-' };
1434 static int m4
[] = { 1, S_IWGRP
, 'w', '-' };
1435 static int m5
[] = { 4, S_ISGID
|S_IXGRP
, 's', S_IXGRP
,
1436 'x', S_ISGID
|LS_NOTREG
, 'S',
1442 static int m6
[] = { 1, S_IROTH
, 'r', '-' };
1443 static int m7
[] = { 1, S_IWOTH
, 'w', '-' };
1444 static int m8
[] = { 3, S_ISVTX
|S_IXOTH
, 't', S_IXOTH
,
1445 'x', S_ISVTX
, 'T', '-'};
1447 static int *m
[] = { m0
, m1
, m2
, m3
, m4
, m5
, m6
, m7
, m8
};
1452 for (mp
= &m
[0]; mp
< &m
[sizeof (m
) / sizeof (m
[0])]; mp
++)
1457 selection(int *pairp
)
1463 if ((flags
& *pairp
) == *pairp
) {
1470 (void) putchar(*pairp
);
1475 * column: get to the beginning of the next column.
1483 (void) putc(',', stdout
);
1485 if (curcol
+ colwidth
+ 2 > num_cols
) {
1486 (void) putc('\n', stdout
);
1490 (void) putc(' ', stdout
);
1495 (void) putc('\n', stdout
);
1499 if ((curcol
/ colwidth
+ 2) * colwidth
> num_cols
) {
1500 (void) putc('\n', stdout
);
1505 (void) putc(' ', stdout
);
1507 } while (curcol
% colwidth
);
1515 (void) putc('\n', stdout
);
1521 * read each filename in directory dir and store its
1522 * status in flist[nfiles]
1523 * use makename() to form pathname dir/filename;
1526 rddir(char *dir
, struct ditem
*myinfo
)
1528 struct dirent
*dentry
;
1534 if ((dirf
= opendir(dir
)) == NULL
) {
1535 (void) fflush(stdout
);
1543 if ((dentry
= readdir(dirf
)) == NULL
)
1545 if (aflg
== 0 && dentry
->d_name
[0] == '.' &&
1547 dentry
->d_name
[1] == '\0' ||
1548 dentry
->d_name
[1] == '.' &&
1549 dentry
->d_name
[2] == '\0'))
1551 * check for directory items '.', '..',
1552 * and items without valid inode-number;
1556 /* skip entries ending in ~ if -B was given */
1558 dentry
->d_name
[strlen(dentry
->d_name
) - 1] == '~')
1561 width
= strcol((unsigned char *)dentry
->d_name
);
1562 if (width
> filewidth
)
1565 ep
= gstat(makename(dir
, dentry
->d_name
), 0, myinfo
);
1571 ep
->lnum
= dentry
->d_ino
;
1572 for (j
= 0; dentry
->d_name
[j
] != '\0'; j
++)
1573 ep
->ln
.lname
[j
] = dentry
->d_name
[j
];
1574 ep
->ln
.lname
[j
] = '\0';
1578 int sav_errno
= errno
;
1580 (void) fprintf(stderr
,
1581 gettext("ls: error reading directory %s: %s\n"),
1582 dir
, strerror(sav_errno
));
1584 (void) closedir(dirf
);
1585 colwidth
= fixedwidth
+ filewidth
;
1590 * Attaching a link to an inode's ancestors. Search
1591 * through the ancestors to check for cycles (an inode which
1592 * we have already tracked in this inodes ancestry). If a cycle
1593 * is detected, set the exit code and record the fact so that
1594 * it is reported at the right time when printing the directory.
1595 * In addition, set the exit code. Note: If the -a flag was
1596 * specified, we don't want to check for cycles for directories
1597 * ending in '/.' or '/..' unless they were specified on the
1601 record_ancestry(char *file
, struct stat
*pstatb
, struct lbuf
*rep
,
1602 int argfl
, struct ditem
*myparent
)
1605 struct ditem
*myinfo
;
1608 file_len
= strlen(file
);
1609 if (!aflg
|| argfl
|| (NOTWORKINGDIR(file
, file_len
) &&
1610 NOTPARENTDIR(file
, file_len
))) {
1612 * Add this inode's ancestry
1613 * info and insert it into the
1614 * ancestry list by pointing
1615 * back to its parent. We save
1616 * it (in rep) with the other info
1617 * we're gathering for this inode.
1619 if ((myinfo
= malloc(
1620 sizeof (struct ditem
))) == NULL
) {
1624 myinfo
->dev
= pstatb
->st_dev
;
1625 myinfo
->ino
= pstatb
->st_ino
;
1626 myinfo
->parent
= myparent
;
1627 rep
->ancinfo
= myinfo
;
1630 * If this node has the same device id and
1631 * inode number of one of its ancestors,
1632 * then we've detected a cycle.
1634 if (myparent
!= NULL
) {
1635 for (tptr
= myparent
; tptr
->parent
!= NULL
;
1636 tptr
= tptr
->parent
) {
1637 if ((tptr
->dev
== pstatb
->st_dev
) &&
1638 (tptr
->ino
== pstatb
->st_ino
)) {
1640 * Cycle detected for this
1641 * directory. Record the fact
1642 * it is a cycle so we don't
1643 * try to process this
1644 * directory as we are
1645 * walking through the
1646 * list of directories.
1658 * Do re-calculate the mode for group for ACE_T type of acls.
1659 * This is because, if the server's FS happens to be UFS, supporting
1660 * POSIX ACL's, then it does a special calculation of group mode
1661 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1663 * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1664 * algorithm is used for the group mode calculation only.
1665 * What is modified here from the algorithm is that only the
1666 * entries with flags ACE_GROUP are considered. For each entry
1667 * with ACE_GROUP flag, the first occurance of a specific access
1668 * is checked if it is allowed.
1669 * We are not interested in perms for user and other, as they
1670 * were taken from st_mode value.
1671 * We are not interested in a_who field of ACE, as we need just
1672 * unix mode bits for the group.
1675 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP)
1676 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1679 grp_mask_to_mode(acl_t
*acep
)
1681 int mode
= 0, seen
= 0;
1686 acecnt
= acl_cnt(acep
);
1687 for (ap
= (ace_t
*)acl_data(acep
); acecnt
--; ap
++) {
1689 if (ap
->a_type
!= ACE_ACCESS_ALLOWED_ACE_TYPE
&&
1690 ap
->a_type
!= ACE_ACCESS_DENIED_ACE_TYPE
)
1693 if (ap
->a_flags
& ACE_INHERIT_ONLY_ACE
)
1697 * if it is first group@ or first everyone@
1698 * for each of read, write and execute, then
1699 * that will be the group mode bit.
1701 flags
= ap
->a_flags
& ACE_TYPE_FLAGS
;
1702 if (flags
== OWNED_GROUP
|| flags
== ACE_EVERYONE
) {
1703 if (ap
->a_access_mask
& ACE_READ_DATA
) {
1704 if (!(seen
& S_IRGRP
)) {
1706 if (IS_TYPE_ALLOWED(ap
->a_type
))
1710 if (ap
->a_access_mask
& ACE_WRITE_DATA
) {
1711 if (!(seen
& S_IWGRP
)) {
1713 if (IS_TYPE_ALLOWED(ap
->a_type
))
1717 if (ap
->a_access_mask
& ACE_EXECUTE
) {
1718 if (!(seen
& S_IXGRP
)) {
1720 if (IS_TYPE_ALLOWED(ap
->a_type
))
1730 * get status of file and recomputes tblocks;
1731 * argfl = 1 if file is a name in ls-command and = 0
1732 * for filename in a directory whose name is an
1733 * argument in the command;
1734 * stores a pointer in flist[nfiles] and
1735 * returns that pointer;
1736 * returns NULL if failed;
1738 static struct lbuf
*
1739 gstat(char *file
, int argfl
, struct ditem
*myparent
)
1741 struct stat statb
, statb1
;
1745 int (*statf
)() = ((Lflg
) || (Hflg
&& argfl
)) ? stat
: lstat
;
1749 o_mode_t groupperm
, mask
;
1750 int grouppermfound
, maskfound
;
1755 if (nfiles
>= maxfils
) {
1757 * all flist/lbuf pair assigned files, time to get some
1761 if (((flist
= realloc(flist
,
1762 maxfils
* sizeof (struct lbuf
*))) == NULL
) ||
1763 ((nxtlbf
= malloc(quantn
*
1764 sizeof (struct lbuf
))) == NULL
)) {
1772 * nfiles is reset to nargs for each directory
1773 * that is given as an argument maxn is checked
1774 * to prevent the assignment of an lbuf to a flist entry
1775 * that already has one assigned.
1777 if (nfiles
>= maxn
) {
1779 flist
[nfiles
++] = rep
;
1782 rep
= flist
[nfiles
++];
1787 rep
->lflags
= (mode_t
)0;
1788 rep
->flinkto
= NULL
;
1790 rep
->lat
.tv_sec
= time(NULL
);
1791 rep
->lat
.tv_nsec
= 0;
1792 rep
->lct
.tv_sec
= time(NULL
);
1793 rep
->lct
.tv_nsec
= 0;
1794 rep
->lmt
.tv_sec
= time(NULL
);
1795 rep
->lmt
.tv_nsec
= 0;
1799 rep
->link_color
= NULL
;
1801 if (argfl
|| statreq
) {
1809 if ((*statf
)(file
, &statb
) < 0) {
1810 if (argfl
|| errno
!= ENOENT
||
1811 (Lflg
&& lstat(file
, &statb
) == 0)) {
1813 * Avoid race between readdir and lstat.
1814 * Print error message in case of dangling link.
1824 * If -H was specified, and the file linked to was
1825 * not a directory, then we need to get the info
1826 * for the symlink itself.
1828 if ((Hflg
) && (argfl
) &&
1829 ((statb
.st_mode
& S_IFMT
) != S_IFDIR
)) {
1830 if (lstat(file
, &statb
) < 0) {
1836 rep
->lnum
= statb
.st_ino
;
1837 rep
->lsize
= statb
.st_size
;
1838 rep
->lblocks
= statb
.st_blocks
;
1840 rep
->color
= ls_color_find(file
, statb
.st_mode
);
1842 switch (statb
.st_mode
& S_IFMT
) {
1846 record_ancestry(file
, &statb
, rep
,
1852 rep
->lsize
= (off_t
)statb
.st_rdev
;
1856 rep
->lsize
= (off_t
)statb
.st_rdev
;
1866 /* symbolic links may not have ACLs, so elide acl() */
1867 if ((Lflg
== 0) || (Hflg
== 0) ||
1868 ((Hflg
) && (!argfl
))) {
1872 if (lflg
|| colorflg
) {
1873 cc
= readlink(file
, buf
, BUFSIZ
);
1878 * follow the symbolic link
1879 * to generate the appropriate
1880 * Fflg marker for the object
1881 * eg, /bin -> /sym/bin/
1884 if (Fflg
|| pflg
|| colorflg
)
1885 error
= stat(file
, &statb1
);
1897 if ((Fflg
|| pflg
) && error
>= 0) {
1898 switch (statb1
.st_mode
& S_IFMT
) {
1912 if ((statb1
.st_mode
& ~S_IFMT
) &
1913 (S_IXUSR
|S_IXGRP
| S_IXOTH
))
1919 rep
->flinkto
= strdup(buf
);
1924 * ls /sym behaves differently from ls /sym/
1925 * when /sym is a symbolic link. This is fixed
1926 * when explicit arguments are specified.
1930 /* Do not follow a symlink when -F is specified */
1931 if ((!argfl
) || (argfl
&& Fflg
) ||
1932 (stat(file
, &statb1
) < 0))
1934 /* Follow a symlink when -F is specified */
1935 if (!argfl
|| stat(file
, &statb1
) < 0)
1938 if ((statb1
.st_mode
& S_IFMT
) == S_IFDIR
) {
1941 rep
->lsize
= statb1
.st_size
;
1943 record_ancestry(file
, &statb
, rep
,
1961 rep
->lflags
= statb
.st_mode
& ~S_IFMT
;
1963 if (!S_ISREG(statb
.st_mode
))
1964 rep
->lflags
|= LS_NOTREG
;
1966 rep
->luid
= statb
.st_uid
;
1967 rep
->lgid
= statb
.st_gid
;
1968 rep
->lnl
= statb
.st_nlink
;
1969 if (uflg
|| (tmflg
&& atm
))
1970 rep
->lmtime
= statb
.st_atim
;
1971 else if (cflg
|| (tmflg
&& ctm
))
1972 rep
->lmtime
= statb
.st_ctim
;
1974 rep
->lmtime
= statb
.st_mtim
;
1975 rep
->lat
= statb
.st_atim
;
1976 rep
->lct
= statb
.st_ctim
;
1977 rep
->lmt
= statb
.st_mtim
;
1979 /* ACL: check acl entries count */
1982 error
= acl_get(file
, 0, &rep
->aclp
);
1984 (void) fprintf(stderr
,
1985 gettext("ls: can't read ACL on %s: %s\n"),
1986 file
, acl_strerror(error
));
1995 ((acl_flags(rep
->aclp
) & ACL_IS_TRIVIAL
) == 0)) {
1998 * Special handling for ufs aka aclent_t ACL's
2000 if (acl_type(rep
->aclp
) == ACLENT_T
) {
2002 * For files with non-trivial acls, the
2003 * effective group permissions are the
2004 * intersection of the GROUP_OBJ value
2005 * and the CLASS_OBJ (acl mask) value.
2006 * Determine both the GROUP_OBJ and
2007 * CLASS_OBJ for this file and insert
2008 * the logical AND of those two values
2009 * in the group permissions field
2010 * of the lflags value for this file.
2014 * Until found in acl list, assume
2015 * maximum permissions for both group
2016 * a nd mask. (Just in case the acl
2017 * lacks either value for some reason.)
2023 aclcnt
= acl_cnt(rep
->aclp
);
2025 (aclent_t
*)acl_data(rep
->aclp
);
2027 if (tp
->a_type
== GROUP_OBJ
) {
2028 groupperm
= tp
->a_perm
;
2032 if (tp
->a_type
== CLASS_OBJ
) {
2036 if (grouppermfound
&& maskfound
)
2041 /* reset all the group bits */
2042 rep
->lflags
&= ~S_IRWXG
;
2045 * Now set them to the logical AND of
2046 * the GROUP_OBJ permissions and the
2050 rep
->lflags
|= (groupperm
& mask
) << 3;
2052 } else if (acl_type(rep
->aclp
) == ACE_T
) {
2054 mode
= grp_mask_to_mode(rep
->aclp
);
2055 rep
->lflags
&= ~S_IRWXG
;
2056 rep
->lflags
|= mode
;
2060 if (!vflg
&& !Vflg
&& rep
->aclp
) {
2061 acl_free(rep
->aclp
);
2065 if (atflg
&& pathconf(file
, _PC_XATTR_EXISTS
) == 1)
2071 /* mask ISARG and other file-type bits */
2073 if (rep
->ltype
!= 'b' && rep
->ltype
!= 'c')
2074 tblocks
+= rep
->lblocks
;
2076 /* Get extended system attributes */
2078 if ((saflg
|| (tmflg
&& crtm
) || (tmflg
&& alltm
)) &&
2079 (sysattr_support(file
, _PC_SATTR_EXISTS
) == 1)) {
2082 sacnt
= attr_count();
2084 * Allocate 'sacnt' size array to hold extended
2085 * system attribute name (verbose) or respective
2086 * symbol represenation (compact).
2088 rep
->exttr
= xmalloc(sacnt
* sizeof (struct attrb
),
2091 /* initialize boolean attribute list */
2092 for (i
= 0; i
< sacnt
; i
++)
2093 rep
->exttr
[i
].name
= NULL
;
2094 if (get_sysxattr(file
, rep
) != 0) {
2095 (void) fprintf(stderr
,
2096 gettext("ls:Failed to retrieve "
2097 "extended system attribute from "
2099 rep
->exttr
[0].name
= xmalloc(2, rep
);
2100 (void) strlcpy(rep
->exttr
[0].name
, "?", 2);
2108 * returns pathname of the form dir/file;
2109 * dir and file are null-terminated strings.
2112 makename(char *dir
, char *file
)
2115 * PATH_MAX is the maximum length of a path name.
2116 * MAXNAMLEN is the maximum length of any path name component.
2117 * Allocate space for both, plus the '/' in the middle
2118 * and the null character at the end.
2119 * dfile is static as this is returned by makename().
2121 static char dfile
[PATH_MAX
+ 1 + MAXNAMLEN
+ 1];
2128 if (dp
> dfile
&& *(dp
- 1) != '/')
2144 #define NMAX (sizeof (utmp.ut_name))
2145 #define SCPYN(a, b) (void) strncpy(a, b, NMAX)
2148 struct cachenode
{ /* this struct must be zeroed before using */
2149 struct cachenode
*lesschild
; /* subtree whose entries < val */
2150 struct cachenode
*grtrchild
; /* subtree whose entries > val */
2151 long val
; /* the uid or gid of this entry */
2152 int initted
; /* name has been filled in */
2153 char name
[NMAX
+1]; /* the string that val maps to */
2155 static struct cachenode
*names
, *groups
;
2157 static struct cachenode
*
2158 findincache(struct cachenode
**head
, long val
)
2160 struct cachenode
**parent
= head
;
2161 struct cachenode
*c
= *parent
;
2164 if (val
== c
->val
) {
2167 } else if (val
< c
->val
) {
2168 parent
= &c
->lesschild
;
2171 parent
= &c
->grtrchild
;
2176 /* not in the cache, make a new entry for it */
2177 c
= calloc(1, sizeof (struct cachenode
));
2188 * get name from cache, or passwd file for a given uid;
2189 * lastuid is set to uid.
2194 struct passwd
*pwent
;
2195 struct cachenode
*c
;
2197 if ((uid
== lastuid
) && lastuname
)
2200 c
= findincache(&names
, uid
);
2201 if (c
->initted
== 0) {
2202 if ((pwent
= getpwuid(uid
)) != NULL
) {
2203 SCPYN(&c
->name
[0], pwent
->pw_name
);
2205 (void) sprintf(&c
->name
[0], "%-8u", (int)uid
);
2210 lastuname
= &c
->name
[0];
2215 * get name from cache, or group file for a given gid;
2216 * lastgid is set to gid.
2221 struct group
*grent
;
2222 struct cachenode
*c
;
2224 if ((gid
== lastgid
) && lastgname
)
2227 c
= findincache(&groups
, gid
);
2228 if (c
->initted
== 0) {
2229 if ((grent
= getgrgid(gid
)) != NULL
) {
2230 SCPYN(&c
->name
[0], grent
->gr_name
);
2232 (void) sprintf(&c
->name
[0], "%-8u", (int)gid
);
2237 lastgname
= &c
->name
[0];
2241 /* return >0 if item pointed by pp2 should appear first */
2243 compar(struct lbuf
**pp1
, struct lbuf
**pp2
)
2245 struct lbuf
*p1
, *p2
;
2251 * compare two names in ls-command one of which is file
2252 * and the other is a directory;
2253 * this portion is not used for comparing files within
2254 * a directory name of ls-command;
2256 if (p1
->lflags
&ISARG
&& p1
->ltype
== 'd') {
2257 if (!(p2
->lflags
&ISARG
&& p2
->ltype
== 'd'))
2260 if (p2
->lflags
&ISARG
&& p2
->ltype
== 'd')
2265 if (p2
->lmtime
.tv_sec
> p1
->lmtime
.tv_sec
)
2267 else if (p2
->lmtime
.tv_sec
< p1
->lmtime
.tv_sec
)
2269 /* times are equal to the sec, check nsec */
2270 if (p2
->lmtime
.tv_nsec
> p1
->lmtime
.tv_nsec
)
2272 else if (p2
->lmtime
.tv_nsec
< p1
->lmtime
.tv_nsec
)
2274 /* if times are equal, fall through and sort by name */
2277 * The size stored in lsize can be either the
2278 * size or the major minor number (in the case of
2279 * block and character special devices). If it's
2280 * a major minor number, then the size is considered
2281 * to be zero and we want to fall through and sort
2282 * by name. In addition, if the size of p2 is equal
2283 * to the size of p1 we want to fall through and
2286 off_t p1size
= (p1
->ltype
== 'b') ||
2287 (p1
->ltype
== 'c') ? 0 : p1
->lsize
;
2288 off_t p2size
= (p2
->ltype
== 'b') ||
2289 (p2
->ltype
== 'c') ? 0 : p2
->lsize
;
2290 if (p2size
> p1size
) {
2292 } else if (p2size
< p1size
) {
2295 /* Sizes are equal, fall through and sort by name. */
2297 return (rflg
* strcoll(
2298 p1
->lflags
& ISARG
? p1
->ln
.namep
: p1
->ln
.lname
,
2299 p2
->lflags
&ISARG
? p2
->ln
.namep
: p2
->ln
.lname
));
2303 pprintf(char *s1
, char *s2
)
2305 csi_pprintf((unsigned char *)s1
);
2306 csi_pprintf((unsigned char *)s2
);
2310 csi_pprintf(unsigned char *s
)
2319 if (!qflg
&& !bflg
) {
2320 for (cp
= s
; *cp
!= '\0'; cp
++) {
2321 (void) putchar(*cp
);
2327 for (cp
= s
; *cp
; ) {
2328 if (isascii(c
= *cp
)) {
2334 (void) putc('\\', stdout
);
2335 c
= '0' + ((*cp
>> 6) & 07);
2336 (void) putc(c
, stdout
);
2337 c
= '0' + ((*cp
>> 3) & 07);
2338 (void) putc(c
, stdout
);
2339 c
= '0' + (*cp
& 07);
2344 (void) putc(c
, stdout
);
2348 if ((c_len
= mbtowc(&pcode
, (char *)cp
, MB_LEN_MAX
)) <= 0) {
2353 if ((p_col
= wcwidth(pcode
)) > 0) {
2354 (void) putwchar(pcode
);
2361 for (i
= 0; i
< c_len
; i
++) {
2366 (void) putc('\\', stdout
);
2367 c
= '0' + ((*cp
>> 6) & 07);
2368 (void) putc(c
, stdout
);
2369 c
= '0' + ((*cp
>> 3) & 07);
2370 (void) putc(c
, stdout
);
2371 c
= '0' + (*cp
& 07);
2374 (void) putc(c
, stdout
);
2381 strcol(unsigned char *s1
)
2396 if ((len
= mbtowc(&wc
, (char *)s1
, MB_LEN_MAX
)) <= 0) {
2402 if ((w_col
= wcwidth(wc
)) < 0)
2411 * Convert an unsigned long long to a string representation and place the
2412 * result in the caller-supplied buffer.
2414 * The number provided is a size in bytes. The number is first
2415 * converted to an integral multiple of 'scale' bytes. This new
2416 * number is then scaled down until it is small enough to be in a good
2417 * human readable format, i.e. in the range 0 thru scale-1. If the
2418 * number used to derive the final number is not a multiple of scale, and
2419 * the final number has only a single significant digit, we compute
2420 * tenths of units to provide a second significant digit.
2422 * The value "(unsigned long long)-1" is a special case and is always
2423 * converted to "-1".
2425 * A pointer to the caller-supplied buffer is returned.
2428 number_to_scaled_string(
2429 numbuf_t buf
, /* put the result here */
2430 unsigned long long number
, /* convert this number */
2433 unsigned long long save
;
2434 /* Measurement: kilo, mega, giga, tera, peta, exa */
2435 char *uom
= "KMGTPE";
2437 if ((long long)number
== (long long)-1) {
2438 (void) strlcpy(buf
, "-1", sizeof (numbuf_t
));
2443 number
= number
/ scale
;
2446 * Now we have number as a count of scale units.
2447 * If no further scaling is necessary, we round up as appropriate.
2449 * The largest value number could have had entering the routine is
2450 * 16 Exabytes, so running off the end of the uom array should
2451 * never happen. We check for that, though, as a guard against
2452 * a breakdown elsewhere in the algorithm.
2454 if (number
< (unsigned long long)scale
) {
2455 if ((save
% scale
) >= (unsigned long long)(scale
/ 2)) {
2456 if (++number
== (unsigned long long)scale
) {
2462 while ((number
>= (unsigned long long)scale
) && (*uom
!= 'E')) {
2463 uom
++; /* next unit of measurement */
2466 * If we're over half way to the next unit of
2467 * 'scale' bytes (which means we should round
2468 * up), then adding half of 'scale' prior to
2469 * the division will push us into that next
2470 * unit of scale when we perform the division
2472 number
= (number
+ (scale
/ 2)) / scale
;
2476 /* check if we should output a decimal place after the point */
2477 if ((save
/ scale
) < 10) {
2478 /* snprintf() will round for us */
2479 float fnum
= (float)save
/ scale
;
2480 (void) snprintf(buf
, sizeof (numbuf_t
), "%2.1f%c",
2483 (void) snprintf(buf
, sizeof (numbuf_t
), "%4llu%c",
2489 /* Get extended system attributes and set the display */
2492 get_sysxattr(char *fname
, struct lbuf
*rep
)
2500 if ((error
= getattrat(AT_FDCWD
, XATTR_VIEW_READWRITE
, fname
,
2502 perror("ls:getattrat");
2507 * Allocate 'sacnt' size array to hold extended timestamp
2508 * system attributes and initialize the array.
2510 rep
->extm
= xmalloc(sacnt
* sizeof (struct attrtm
), rep
);
2511 for (i
= 0; i
< sacnt
; i
++) {
2512 rep
->extm
[i
].stm
= 0;
2513 rep
->extm
[i
].nstm
= 0;
2514 rep
->extm
[i
].name
= NULL
;
2516 while ((pair
= nvlist_next_nvpair(response
, pair
)) != NULL
) {
2517 name
= nvpair_name(pair
);
2518 type
= nvpair_type(pair
);
2519 if (type
== DATA_TYPE_BOOLEAN_VALUE
) {
2520 error
= nvpair_value_boolean_value(pair
, &value
);
2522 (void) fprintf(stderr
,
2523 gettext("nvpair_value_boolean_value "
2524 "failed: error = %d\n"), error
);
2528 set_sysattrb_display(name
, value
, rep
);
2530 } else if (type
== DATA_TYPE_UINT64_ARRAY
) {
2532 set_sysattrtm_display(name
, rep
);
2536 nvlist_free(response
);
2540 /* Set extended system attribute boolean display */
2543 set_sysattrb_display(char *name
, boolean_t val
, struct lbuf
*rep
)
2549 fattr
= name_to_attr(name
);
2550 if (fattr
!= F_ATTR_INVAL
&& fattr
< sacnt
) {
2554 rep
->exttr
[fattr
].name
= xmalloc(len
+ 1, rep
);
2555 (void) strlcpy(rep
->exttr
[fattr
].name
, name
,
2558 rep
->exttr
[fattr
].name
= xmalloc(len
+ 3, rep
);
2559 (void) snprintf(rep
->exttr
[fattr
].name
, len
+ 3,
2563 opt
= attr_to_option(fattr
);
2566 rep
->exttr
[fattr
].name
= xmalloc(len
+ 1, rep
);
2568 (void) strlcpy(rep
->exttr
[fattr
].name
,
2571 (void) strlcpy(rep
->exttr
[fattr
].name
,
2578 /* Set extended system attribute timestamp display */
2581 set_sysattrtm_display(char *name
, struct lbuf
*rep
)
2588 if (nvpair_value_uint64_array(pair
, &value
, &nelem
) == 0) {
2589 if (*value
!= NULL
) {
2592 while (rep
->extm
[i
].stm
!= 0 && i
< sacnt
)
2594 rep
->extm
[i
].stm
= value
[0];
2595 rep
->extm
[i
].nstm
= value
[1];
2596 rep
->extm
[i
].name
= xmalloc(len
+ 1, rep
);
2597 (void) strlcpy(rep
->extm
[i
].name
, name
, len
+ 1);
2603 format_time(time_t sec
, time_t nsec
)
2605 const char *fstr
= time_fmt_new
;
2606 char fmt_buf
[FMTSIZE
];
2609 (void) snprintf(fmt_buf
, FMTSIZE
, fstr
, nsec
);
2610 (void) strftime(time_buf
, sizeof (time_buf
), fmt_buf
,
2615 if (sec
< year
|| sec
> now
)
2616 fstr
= time_fmt_old
;
2618 /* if a custom time was specified, shouldn't be localized */
2619 (void) strftime(time_buf
, sizeof (time_buf
),
2620 (time_custom
== 0) ? dcgettext(NULL
, fstr
, LC_TIME
) : fstr
,
2625 format_attrtime(struct lbuf
*p
)
2630 if (p
->extm
!= NULL
) {
2631 for (i
= 0; i
< sacnt
; i
++) {
2632 if (p
->extm
[i
].name
!= NULL
) {
2640 const char *old_save
= time_fmt_old
;
2641 const char *new_save
= time_fmt_new
;
2643 /* Eflg always sets format to FORMAT_ISO_FULL */
2644 if (!Eflg
&& !time_custom
) {
2645 time_fmt_old
= FORMAT_OLD
;
2646 time_fmt_new
= FORMAT_NEW
;
2649 format_time((time_t)p
->extm
[i
].stm
, (time_t)p
->extm
[i
].nstm
);
2651 time_fmt_old
= old_save
;
2652 time_fmt_new
= new_save
;
2657 print_time(struct lbuf
*p
)
2659 const char *old_save
= time_fmt_old
;
2660 const char *new_save
= time_fmt_new
;
2665 time_fmt_old
= FORMAT_LONG
;
2666 time_fmt_new
= FORMAT_LONG
;
2670 format_time(p
->lat
.tv_sec
, p
->lat
.tv_nsec
);
2671 (void) printf(" timestamp: atime %s\n", time_buf
);
2672 format_time(p
->lct
.tv_sec
, p
->lct
.tv_nsec
);
2673 (void) printf(" timestamp: ctime %s\n", time_buf
);
2674 format_time(p
->lmt
.tv_sec
, p
->lmt
.tv_nsec
);
2675 (void) printf(" timestamp: mtime %s\n", time_buf
);
2676 if (p
->extm
!= NULL
) {
2677 while (p
->extm
[i
].nstm
!= 0 && i
< sacnt
) {
2678 format_time(p
->extm
[i
].stm
, p
->extm
[i
].nstm
);
2679 if (p
->extm
[i
].name
!= NULL
) {
2680 (void) printf(" timestamp:"
2682 p
->extm
[i
].name
, time_buf
);
2688 time_fmt_old
= old_save
;
2689 time_fmt_new
= new_save
;
2693 * Check if color definition applies to entry, returns 1 if yes, 0 if no
2696 color_match(const char *fname
, mode_t mode
, ls_color_t
*color
)
2698 switch (color
->ftype
) {
2701 size_t fname_len
, sfx_len
;
2703 fname_len
= strlen(fname
);
2704 sfx_len
= strlen(color
->sfx
);
2705 if (sfx_len
> fname_len
)
2708 if (strcmp(color
->sfx
, fname
+ fname_len
- sfx_len
) == 0)
2718 return (S_ISREG(mode
));
2721 return (S_ISDIR(mode
));
2724 return (S_ISLNK(mode
));
2727 return (S_ISFIFO(mode
));
2730 return (S_ISSOCK(mode
));
2733 return (S_ISDOOR(mode
));
2736 return (S_ISBLK(mode
));
2739 return (S_ISCHR(mode
));
2742 return (S_ISPORT(mode
));
2745 /* this is tested for by gstat */
2749 return (!S_ISLNK(mode
) && (mode
& S_ISUID
));
2752 return (!S_ISLNK(mode
) && (mode
& S_ISGID
));
2754 case LS_STICKY_OTHER_WRITABLE
:
2755 return (!S_ISLNK(mode
) && (mode
& (S_IWOTH
|S_ISVTX
)));
2757 case LS_OTHER_WRITABLE
:
2758 return (!S_ISLNK(mode
) && (mode
& S_IWOTH
));
2761 return (!S_ISLNK(mode
) && (mode
& S_ISVTX
));
2764 return (!S_ISLNK(mode
) && (mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)));
2771 dump_color(ls_color_t
*c
)
2776 (void) printf("\n\ttype: ");
2779 (void) printf("LS_NORMAL");
2782 (void) printf("LS_FILE");
2785 (void) printf("LS_EXEC");
2788 (void) printf("LS_DIR");
2791 (void) printf("LS_LINK");
2795 (void) printf("LS_FIFO");
2799 (void) printf("LS_SOCK");
2803 (void) printf("LS_DOOR");
2807 (void) printf("LS_BLK");
2811 (void) printf("LS_CHR");
2815 (void) printf("LS_PORT");
2819 (void) printf("LS_STICKY");
2823 (void) printf("LS_ORPHAN");
2827 (void) printf("LS_SETGID");
2831 (void) printf("LS_SETUID");
2834 case LS_OTHER_WRITABLE
:
2835 (void) printf("LS_OTHER_WRITABLE");
2838 case LS_STICKY_OTHER_WRITABLE
:
2839 (void) printf("LS_STICKY_OTHER_WRITABLE");
2843 (void) printf("LS_PAT\n");
2844 (void) printf("\tpattern: %s", c
->sfx
);
2847 (void) printf("\n");
2848 (void) printf("\tattr: %d\n", c
->attr
);
2849 (void) printf("\tfg: %d\n", c
->fg
);
2850 (void) printf("\tbg: %d\n", c
->bg
);
2851 (void) printf("\t");
2855 ls_color_find(const char *fname
, mode_t mode
)
2860 * Colors are sorted from most general lsc_colors[0] to most specific
2861 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with
2862 * most specific color rule and work towards most general.
2864 for (i
= lsc_ncolors
- 1; i
>= 0; --i
)
2865 if (color_match(fname
, mode
, &lsc_colors
[i
]))
2866 return (&lsc_colors
[i
]);
2872 ls_tprint(char *str
, long int p1
, long int p2
, long int p3
, long int p4
,
2873 long int p5
, long int p6
, long int p7
, long int p8
, long int p9
)
2880 s
= tparm(str
, p1
, p2
, p3
, p4
, p5
, p6
, p7
, p8
, p9
);
2887 ls_start_color(ls_color_t
*c
)
2895 if (c
->attr
& LSA_BOLD
)
2896 ls_tprint(lsc_bold
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2897 if (c
->attr
& LSA_UNDERSCORE
)
2898 ls_tprint(lsc_underline
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2899 if (c
->attr
& LSA_BLINK
)
2900 ls_tprint(lsc_blink
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2901 if (c
->attr
& LSA_REVERSE
)
2902 ls_tprint(lsc_reverse
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2903 if (c
->attr
& LSA_CONCEALED
)
2904 ls_tprint(lsc_concealed
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2905 if (c
->attr
== LSA_NONE
)
2906 ls_tprint(lsc_none
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2909 ls_tprint(lsc_setfg
, c
->fg
, 0, 0, 0, 0, 0, 0, 0, 0);
2911 ls_tprint(lsc_setbg
, c
->bg
, 0, 0, 0, 0, 0, 0, 0, 0);
2917 ls_tprint(lsc_none
, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2919 dump_color(lsc_match
);
2923 new_color_entry(char *colorstr
)
2925 static const struct {
2929 { "no", LS_NORMAL
},
2938 { "or", LS_ORPHAN
},
2939 { "su", LS_SETUID
},
2940 { "sg", LS_SETGID
},
2941 { "tw", LS_STICKY_OTHER_WRITABLE
},
2942 { "ow", LS_OTHER_WRITABLE
},
2943 { "st", LS_STICKY
},
2953 p
= strtok_r(colorstr
, "=", &lasts
);
2960 lsc_colors
[lsc_ncolors
].ftype
= LS_PAT
;
2961 /* don't include the * in the suffix */
2962 if ((lsc_colors
[lsc_ncolors
].sfx
= strdup(p
+ 1)) == NULL
) {
2967 lsc_colors
[lsc_ncolors
].sfx
= NULL
;
2969 for (i
= 0; type_map
[i
].s
!= NULL
; ++i
) {
2970 if (strncmp(type_map
[i
].s
, p
, 2) == 0)
2974 /* ignore unknown file types */
2975 if (type_map
[i
].s
== NULL
)
2978 lsc_colors
[lsc_ncolors
].ftype
= type_map
[i
].stype
;
2982 lsc_colors
[lsc_ncolors
].fg
= -1;
2983 lsc_colors
[lsc_ncolors
].bg
= -1;
2984 for (p
= strtok_r(NULL
, ";", &lasts
); p
!= NULL
;
2985 p
= strtok_r(NULL
, ";", &lasts
)) {
2986 color
= strtol(p
, NULL
, 10);
2997 attr
|= LSA_UNDERSCORE
;
3003 attr
|= LSA_REVERSE
;
3006 attr
|= LSA_CONCEALED
;
3014 lsc_colors
[lsc_ncolors
].fg
= color
- 30;
3016 lsc_colors
[lsc_ncolors
].bg
= color
- 40;
3019 lsc_colors
[lsc_ncolors
].attr
= attr
;
3024 ls_color_compare(const void *p1
, const void *p2
)
3026 const ls_color_t
*c1
= (const ls_color_t
*)p1
;
3027 const ls_color_t
*c2
= (const ls_color_t
*)p2
;
3029 int ret
= c1
->ftype
- c2
->ftype
;
3034 if (c1
->ftype
!= LS_PAT
)
3037 return (strcmp(c1
->sfx
, c2
->sfx
));
3043 static char *default_colorstr
= "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3044 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3045 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3046 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3047 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3048 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3049 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3050 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3051 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3052 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3053 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3061 (void) setupterm(NULL
, 1, &termret
);
3065 if ((colorstr
= getenv("LS_COLORS")) == NULL
)
3066 colorstr
= default_colorstr
;
3069 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors
3070 * if there are invalid entries passed in the string (they are ignored)
3073 for (p
= strchr(colorstr
, ':'); p
!= NULL
&& *p
!= '\0';
3074 p
= strchr(++p
, ':'))
3077 lsc_colors
= calloc(color_sz
, sizeof (ls_color_t
));
3078 if (lsc_colors
== NULL
) {
3083 for (p
= strtok_r(colorstr
, ":", &lasts
);
3084 p
!= NULL
&& lsc_ncolors
< color_sz
;
3085 p
= strtok_r(NULL
, ":", &lasts
))
3088 qsort((void *)lsc_colors
, lsc_ncolors
, sizeof (ls_color_t
),
3091 for (i
= 0; i
< lsc_ncolors
; ++i
)
3092 if (lsc_colors
[i
].ftype
== LS_ORPHAN
) {
3093 lsc_orphan
= &lsc_colors
[i
];
3097 if ((lsc_bold
= tigetstr("bold")) == (char *)-1)
3100 if ((lsc_underline
= tigetstr("smul")) == (char *)-1)
3101 lsc_underline
= NULL
;
3103 if ((lsc_blink
= tigetstr("blink")) == (char *)-1)
3106 if ((lsc_reverse
= tigetstr("rev")) == (char *)-1)
3109 if ((lsc_concealed
= tigetstr("prot")) == (char *)-1)
3110 lsc_concealed
= NULL
;
3112 if ((lsc_none
= tigetstr("sgr0")) == (char *)-1)
3115 if ((lsc_setfg
= tigetstr("setaf")) == (char *)-1)
3118 if ((lsc_setbg
= tigetstr("setab")) == (char *)-1)
3121 if (getenv("_LS_COLOR_DEBUG") != NULL
) {
3125 for (i
= 0; i
< lsc_ncolors
; ++i
)
3126 dump_color(&lsc_colors
[i
]);
3130 /* Free extended system attribute lists */
3133 free_sysattr(struct lbuf
*p
)
3137 if (p
->exttr
!= NULL
) {
3138 for (i
= 0; i
< sacnt
; i
++) {
3139 if (p
->exttr
[i
].name
!= NULL
)
3140 free(p
->exttr
[i
].name
);
3144 if (p
->extm
!= NULL
) {
3145 for (i
= 0; i
< sacnt
; i
++) {
3146 if (p
->extm
[i
].name
!= NULL
)
3147 free(p
->extm
[i
].name
);
3153 /* Allocate extended system attribute list */
3156 xmalloc(size_t size
, struct lbuf
*p
)
3158 if ((p
= malloc(size
)) == NULL
) {
3161 nvlist_free(response
);