1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
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
26 */
27
28 /*
29 * Copyright 2009 Jason King. All rights reserved.
30 * Use is subject to license terms.
31 */
32
33 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
34 /* All Rights Reserved */
35
36 /* Copyright (c) 1987, 1988 Microsoft Corporation */
37 /* All Rights Reserved */
38
39 /*
40 * List files or directories
41 */
42
43 #include <sys/param.h>
44 #include <sys/types.h>
45 #include <sys/mkdev.h>
46 #include <sys/stat.h>
47 #include <sys/acl.h>
48
49 #include <wchar.h>
50 #include <stdio.h>
51 #include <ctype.h>
52 #include <dirent.h>
53 #include <string.h>
54 #include <locale.h>
55 #include <curses.h>
56 #include <term.h>
57 #include <termios.h>
58 #include <stdlib.h>
59 #include <widec.h>
60 #include <locale.h>
61 #include <wctype.h>
62 #include <pwd.h>
63 #include <grp.h>
64 #include <limits.h>
65 #include <fcntl.h>
66 #include <unistd.h>
67 #include <libgen.h>
68 #include <errno.h>
69 #include <aclutils.h>
70 #include <libnvpair.h>
71 #include <libcmdutils.h>
72 #include <attr.h>
73 #include <getopt.h>
74 #include <inttypes.h>
75
76 #ifndef STANDALONE
77 #define TERMINFO
78 #endif
79
80 /*
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.
86 */
87 #ifdef NOTERMINFO
88 #undef TERMINFO
89 #endif
90
91 #include <term.h>
92
93 #define BFSIZE 16
94 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
95 #define ISARG 0100000
96
97 /*
98 * this flag has been added to manipulate the display of S instead of 'l' when
99 * the file is not a regular file and when group execution bit is off
100 */
101 #define LS_NOTREG 010000
102
103
104 /*
105 * Date and time formats
106 *
107 * b --- abbreviated month name
108 * e --- day number
109 * Y --- year in the form ccyy
110 * H --- hour(24-hour version)
111 * M --- minute
112 * F --- yyyy-mm-dd
113 * T --- hh:mm:ss
114 * z --- time zone as hours displacement from UTC
115 * note that %F and %z are from the ISO C99 standard and are
116 * not present in older C libraries
117 */
118 #define FORMAT_OLD " %b %e %Y "
119 #define FORMAT_NEW " %b %e %H:%M "
120 #define FORMAT_LONG " %b %e %T %Y "
121 #define FORMAT_ISO_FULL " %%F %%T.%.09ld %%z "
122 #define FORMAT_ISO_LONG " %F %R "
123 #define FORMAT_ISO_NEW " %m-%d %H:%M "
124 #define FORMAT_ISO_OLD " %F "
125
126 #undef BUFSIZ
127 #define BUFSIZ 4096
128 #define NUMBER_WIDTH 40
129 #define FMTSIZE 50
130
131 struct ditem {
132 dev_t dev; /* directory items device number */
133 ino_t ino; /* directory items inode number */
134 struct ditem *parent; /* dir items ptr to its parent's info */
135 };
136 /* Holds boolean extended system attributes */
137 struct attrb {
138 char *name;
139 };
140 /* Holds timestamp extended system attributes */
141 struct attrtm {
142 char *name;
143 uint64_t stm;
144 uint64_t nstm;
145 };
146
147 #define LSA_NONE (0)
148 #define LSA_BOLD (1L << 0)
149 #define LSA_UNDERSCORE (1L << 1)
150 #define LSA_BLINK (1L << 2)
151 #define LSA_REVERSE (1L << 3)
152 #define LSA_CONCEALED (1L << 4)
153
154 /* these should be ordered most general to most specific */
155 typedef enum LS_CFTYPE {
156 LS_NORMAL,
157 LS_FILE,
158 LS_EXEC,
159 LS_DIR,
160 LS_LINK,
161 LS_FIFO,
162 LS_SOCK,
163 LS_DOOR,
164 LS_BLK,
165 LS_CHR,
166 LS_PORT,
167 LS_STICKY,
168 LS_ORPHAN,
169 LS_SETGID,
170 LS_SETUID,
171 LS_OTHER_WRITABLE,
172 LS_STICKY_OTHER_WRITABLE,
173 LS_PAT
174 } ls_cftype_t;
175
176 typedef struct {
177 char *sfx;
178 ls_cftype_t ftype;
179 int attr;
180 int fg;
181 int bg;
182 } ls_color_t;
183
184 struct lbuf {
185 union {
186 char lname[MAXNAMLEN]; /* used for filename in a directory */
187 char *namep; /* for name in ls-command; */
188 } ln;
189 char ltype; /* filetype */
190 ino_t lnum; /* inode number of file */
191 mode_t lflags; /* 0777 bits used as r,w,x permissions */
192 nlink_t lnl; /* number of links to file */
193 uid_t luid;
194 gid_t lgid;
195 off_t lsize; /* filesize or major/minor dev numbers */
196 blkcnt_t lblocks; /* number of file blocks */
197 timestruc_t lmtime;
198 timestruc_t lat;
199 timestruc_t lct;
200 timestruc_t lmt;
201 char *flinkto; /* symbolic link contents */
202 char acl; /* indicate there are additional acl entries */
203 int cycle; /* cycle detected flag */
204 struct ditem *ancinfo; /* maintains ancestor info */
205 acl_t *aclp; /* ACL if present */
206 struct attrb *exttr; /* boolean extended system attributes */
207 struct attrtm *extm; /* timestamp extended system attributes */
208 ls_color_t *color; /* color for entry */
209 ls_color_t *link_color; /* color for symlink */
210 };
211
212 struct dchain {
213 char *dc_name; /* path name */
214 int cycle_detected; /* cycle detected visiting this directory */
215 struct ditem *myancinfo; /* this directory's ancestry info */
216 struct dchain *dc_next; /* next directory in the chain */
217 };
218
219 /*
220 * A numbuf_t is used when converting a number to a string representation
221 */
222 typedef char numbuf_t[NUMBER_WIDTH];
223
224 static struct dchain *dfirst; /* start of the dir chain */
225 static struct dchain *cdfirst; /* start of the current dir chain */
226 static struct dchain *dtemp; /* temporary - used for linking */
227 static char *curdir; /* the current directory */
228
229 static int first = 1; /* true if first line is not yet printed */
230 static int nfiles = 0; /* number of flist entries in current use */
231 static int nargs = 0; /* number of flist entries used for arguments */
232 static int maxfils = 0; /* number of flist/lbuf entries allocated */
233 static int maxn = 0; /* number of flist entries with lbufs asigned */
234 static int quantn = 64; /* allocation growth quantum */
235 static size_t hlbfsz = 1;
236
237 static struct lbuf *nxtlbf; /* ptr to next lbuf to be assigned */
238 static struct lbuf **hlbf; /* lbuf bookkeeping */
239 static struct lbuf **flist; /* ptr to list of lbuf pointers */
240 static struct lbuf *gstat(char *, int, struct ditem *);
241 static char *getname(uid_t);
242 static char *getgroup(gid_t);
243 static char *makename(char *, char *);
244 static void pentry(struct lbuf *);
245 static void column(void);
246 static void pmode(mode_t aflag);
247 static void selection(int *);
248 static void new_line(void);
249 static void rddir(char *, struct ditem *);
250 static int strcol(unsigned char *);
251 static void pem(struct lbuf **, struct lbuf **, int);
252 static void pdirectory(char *, int, int, int, struct ditem *);
253 static struct cachenode *findincache(struct cachenode **, long);
254 static void freecachenodes(void);
255 static void csi_pprintf(unsigned char *);
256 static void pprintf(char *, char *);
257 static int compar(struct lbuf **pp1, struct lbuf **pp2);
258 static char *number_to_scaled_string(numbuf_t buf,
259 unsigned long long number,
260 long scale);
261 static void record_ancestry(char *, struct stat *, struct lbuf *,
262 int, struct ditem *);
263 static void ls_color_init(void);
264 static ls_color_t *ls_color_find(const char *, mode_t);
265 static void ls_start_color(ls_color_t *);
266 static void ls_end_color(void);
267
268 static int aflg;
269 static int atflg;
270 static int bflg;
271 static int cflg;
272 static int dflg;
273 static int eflg;
274 static int fflg;
275 static int gflg;
276 static int hflg;
277 static int iflg;
278 static int lflg;
279 static int mflg;
280 static int nflg;
281 static int oflg;
282 static int pflg;
283 static int qflg;
284 static int rflg = 1; /* init to 1 for special use in compar */
285 static int sflg;
286 static int tflg;
287 static int uflg;
288 static int Uflg;
289 static int wflg;
290 static int xflg;
291 static int Aflg;
292 static int Bflg;
293 static int Cflg;
294 static int Eflg;
295 static int Fflg;
296 static int Hflg;
297 static int Lflg;
298 static int Rflg;
299 static int Sflg;
300 static int vflg;
301 static int Vflg;
302 static int saflg; /* boolean extended system attr. */
303 static int sacnt; /* number of extended system attr. */
304 static int copt;
305 static int vopt;
306 static int tmflg; /* create time ext. system attr. */
307 static int ctm;
308 static int atm;
309 static int mtm;
310 static int crtm;
311 static int alltm;
312 static long hscale;
313 static mode_t flags;
314 static int err = 0; /* Contains return code */
315 static int colorflg;
316 static int file_typeflg;
317 static int noflist = 0;
318
319 static uid_t lastuid = (uid_t)-1;
320 static gid_t lastgid = (gid_t)-1;
321 static char *lastuname = NULL;
322 static char *lastgname = NULL;
323
324 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
325 static int statreq;
326
327 static uint64_t block_size = 1;
328 static char *dotp = ".";
329
330 static u_longlong_t tblocks; /* number of blocks of files in a directory */
331 static time_t year, now;
332
333 static int num_cols = 80;
334 static int colwidth;
335 static int filewidth;
336 static int fixedwidth;
337 static int nomocore;
338 static int curcol;
339
340 static struct winsize win;
341
342 /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
343 static const char *time_fmt_old = FORMAT_OLD; /* non-recent files */
344 static const char *time_fmt_new = FORMAT_NEW; /* recent files */
345 static int time_custom; /* != 0 if a custom format */
346 static char time_buf[FMTSIZE]; /* array to hold day and time */
347
348 static int lsc_debug;
349 static ls_color_t *lsc_match;
350 static ls_color_t *lsc_colors;
351 static size_t lsc_ncolors;
352 static char *lsc_bold;
353 static char *lsc_underline;
354 static char *lsc_blink;
355 static char *lsc_reverse;
356 static char *lsc_concealed;
357 static char *lsc_none;
358 static char *lsc_setfg;
359 static char *lsc_setbg;
360 static ls_color_t *lsc_orphan;
361
362 #define NOTWORKINGDIR(d, l) (((l) < 2) || \
363 (strcmp((d) + (l) - 2, "/.") != 0))
364
365 #define NOTPARENTDIR(d, l) (((l) < 3) || \
366 (strcmp((d) + (l) - 3, "/..") != 0))
367 /* Extended system attributes support */
368 static int get_sysxattr(char *, struct lbuf *);
369 static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
370 static void set_sysattrtm_display(char *, struct lbuf *);
371 static void format_time(time_t, time_t);
372 static void print_time(struct lbuf *);
373 static void format_attrtime(struct lbuf *);
374 static void *xmalloc(size_t, struct lbuf *);
375 static void free_sysattr(struct lbuf *);
376 static nvpair_t *pair;
377 static nvlist_t *response;
378 static int acl_err;
379
380 const struct option long_options[] = {
381 { "all", no_argument, NULL, 'a' },
382 { "almost-all", no_argument, NULL, 'A' },
383 { "escape", no_argument, NULL, 'b' },
384 { "classify", no_argument, NULL, 'F' },
385 { "human-readable", no_argument, NULL, 'h' },
386 { "dereference", no_argument, NULL, 'L' },
387 { "dereference-command-line", no_argument, NULL, 'H' },
388 { "ignore-backups", no_argument, NULL, 'B' },
389 { "inode", no_argument, NULL, 'i' },
390 { "numeric-uid-gid", no_argument, NULL, 'n' },
391 { "no-group", no_argument, NULL, 'o' },
392 { "hide-control-chars", no_argument, NULL, 'q' },
393 { "reverse", no_argument, NULL, 'r' },
394 { "recursive", no_argument, NULL, 'R' },
395 { "size", no_argument, NULL, 's' },
396 { "width", required_argument, NULL, 'w' },
397
398 /* no short options for these */
399 { "block-size", required_argument, NULL, 0 },
400 { "full-time", no_argument, NULL, 0 },
401 { "si", no_argument, NULL, 0 },
402 { "color", optional_argument, NULL, 0 },
403 { "colour", optional_argument, NULL, 0},
404 { "file-type", no_argument, NULL, 0 },
405 { "time-style", required_argument, NULL, 0 },
406
407 {0, 0, 0, 0}
408 };
409
410 int
411 main(int argc, char *argv[])
412 {
413 int c;
414 int i;
415 int width;
416 int amino = 0;
417 int opterr = 0;
418 int option_index = 0;
419 char *told = NULL;
420 struct lbuf *ep;
421 struct lbuf lb;
422 struct ditem *myinfo = NULL;
423
424 (void) setlocale(LC_ALL, "");
425 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
426 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
427 #endif
428 (void) textdomain(TEXT_DOMAIN);
429 #ifdef STANDALONE
430 if (argv[0][0] == '\0')
431 argc = getargv("ls", &argv, 0);
432 #endif
433
434 lb.lmtime.tv_sec = time(NULL);
435 lb.lmtime.tv_nsec = 0;
436 year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
437 now = lb.lmtime.tv_sec + 60;
438 if (isatty(1)) {
439 Cflg = 1;
440 mflg = 0;
441 }
442
443 while ((c = getopt_long(argc, argv,
444 "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
445 &option_index)) != -1)
446 switch (c) {
447 case 0:
448 /* non-short options */
449 if (strcmp(long_options[option_index].name,
450 "color") == 0 ||
451 strcmp(long_options[option_index].name,
452 "colour") == 0) {
453 if (optarg == NULL ||
454 strcmp(optarg, "always") == 0 ||
455 strcmp(optarg, "yes") == 0 ||
456 strcmp(optarg, "force") == 0) {
457 colorflg++;
458 statreq++;
459 continue;
460 }
461
462 if (strcmp(optarg, "auto") == 0 ||
463 strcmp(optarg, "tty") == 0 ||
464 strcmp(optarg, "if-tty") == 0) {
465 if (isatty(1) == 1) {
466 colorflg++;
467 statreq++;
468 }
469 continue;
470 }
471
472 if (strcmp(optarg, "never") == 0 ||
473 strcmp(optarg, "no") == 0 ||
474 strcmp(optarg, "none") == 0) {
475 colorflg = 0;
476 continue;
477 }
478 (void) fprintf(stderr,
479 gettext("Invalid argument '%s' for "
480 "--color\n"), optarg);
481 ++opterr;
482 continue;
483 }
484
485 if (strcmp(long_options[option_index].name,
486 "si") == 0) {
487 hflg++;
488 hscale = 1000;
489 continue;
490 }
491
492 if (strcmp(long_options[option_index].name,
493 "block-size") == 0) {
494 size_t scale_len = strlen(optarg);
495 uint64_t scale = 1;
496 uint64_t kilo = 1024;
497 char scale_c;
498
499 if (scale_len == 0) {
500 (void) fprintf(stderr, gettext(
501 "Invalid block size \'%s\'\n"),
502 optarg);
503 exit(1);
504 }
505
506 scale_c = optarg[scale_len - 1];
507 if (scale_c == 'B') {
508 /* need at least digit, scale, B */
509 if (scale_len < 3) {
510 (void) fprintf(stderr, gettext(
511 "Invalid block size "
512 "\'%s\'\n"), optarg);
513 exit(1);
514 }
515 kilo = 1000;
516 scale_c = optarg[scale_len - 2];
517 if (isdigit(scale_c)) {
518 (void) fprintf(stderr,
519 gettext("Invalid block size"
520 " \'%s\'\n"), optarg);
521 exit(1);
522 }
523 /*
524 * make optarg[scale_len - 1] point to
525 * the scale factor
526 */
527 --scale_len;
528 }
529
530 switch (scale_c) {
531 case 'y':
532 case 'Y':
533 scale *= kilo;
534 /*FALLTHROUGH*/
535 case 'Z':
536 case 'z':
537 scale *= kilo;
538 /*FALLTHROUGH*/
539 case 'E':
540 case 'e':
541 scale *= kilo;
542 /*FALLTHROUGH*/
543 case 'P':
544 case 'p':
545 scale *= kilo;
546 /*FALLTHROUGH*/
547 case 'T':
548 case 't':
549 scale *= kilo;
550 /*FALLTHROUGH*/
551 case 'G':
552 case 'g':
553 scale *= kilo;
554 /*FALLTHROUGH*/
555 case 'M':
556 case 'm':
557 scale *= kilo;
558 /*FALLTHROUGH*/
559 case 'K':
560 case 'k':
561 scale *= kilo;
562 break;
563 default:
564 if (!isdigit(scale_c)) {
565 (void) fprintf(stderr,
566 gettext("Invalid character "
567 "following block size in "
568 "\'%s\'\n"), optarg);
569 exit(1);
570 }
571 }
572
573 /* NULL out scale constant if present */
574 if (scale > 1 && !isdigit(scale_c))
575 optarg[scale_len - 1] = '\0';
576
577 /* Based on testing, this is what GNU ls does */
578 block_size = strtoll(optarg, NULL, 0) * scale;
579 if (block_size < 1) {
580 (void) fprintf(stderr,
581 gettext("Invalid block size "
582 "\'%s\'\n"), optarg);
583 exit(1);
584 }
585 continue;
586 }
587
588 if (strcmp(long_options[option_index].name,
589 "file-type") == 0) {
590 file_typeflg++;
591 Fflg++;
592 statreq++;
593 continue;
594 }
595
596
597 if (strcmp(long_options[option_index].name,
598 "full-time") == 0) {
599 Eflg++;
600 statreq++;
601 eflg = 0;
602 time_fmt_old = FORMAT_ISO_FULL;
603 time_fmt_new = FORMAT_ISO_FULL;
604 continue;
605 }
606
607 if (strcmp(long_options[option_index].name,
608 "time-style") == 0) {
609 /* like -E, but doesn't imply -l */
610 if (strcmp(optarg, "full-iso") == 0) {
611 Eflg++;
612 statreq++;
613 eflg = 0;
614 time_fmt_old = FORMAT_ISO_FULL;
615 time_fmt_new = FORMAT_ISO_FULL;
616 continue;
617 }
618 if (strcmp(optarg, "long-iso") == 0) {
619 statreq++;
620 Eflg = 0;
621 eflg = 0;
622 time_fmt_old = FORMAT_ISO_LONG;
623 time_fmt_new = FORMAT_ISO_LONG;
624 continue;
625 }
626 if (strcmp(optarg, "iso") == 0) {
627 statreq++;
628 Eflg = 0;
629 eflg = 0;
630 time_fmt_old = FORMAT_ISO_OLD;
631 time_fmt_new = FORMAT_ISO_NEW;
632 continue;
633 }
634 /* should be the default */
635 if (strcmp(optarg, "locale") == 0) {
636 time_fmt_old = FORMAT_OLD;
637 time_fmt_new = FORMAT_NEW;
638 continue;
639 }
640 if (optarg[0] == '+') {
641 char *tnew;
642 char *p;
643 size_t timelen = strlen(optarg);
644
645 p = strchr(optarg, '\n');
646 if (p != NULL)
647 *p++ = '\0';
648
649 /*
650 * Time format requires a leading and
651 * trailing space
652 * Add room for 3 spaces + 2 nulls
653 * The + in optarg is replaced with
654 * a space.
655 */
656 timelen += 2 + 3;
657 told = realloc(told, timelen);
658 if (told == NULL) {
659 perror("ls");
660 exit(2);
661 }
662
663 (void) memset(told, 0, timelen);
664 told[0] = ' ';
665 (void) strlcat(told, &optarg[1],
666 timelen);
667 (void) strlcat(told, " ", timelen);
668
669 if (p != NULL) {
670 size_t tnew_len;
671 size_t told_len =strlen(told);
672
673 tnew = told + told_len + 1;
674 tnew_len = timelen -
675 told_len - 1;
676
677 tnew[0] = ' ';
678 (void) strlcat(tnew, p,
679 tnew_len);
680 (void) strlcat(tnew, " ",
681 tnew_len);
682 time_fmt_new =
683 (const char *)tnew;
684 } else {
685 time_fmt_new =
686 (const char *)told;
687 }
688
689 time_fmt_old = (const char *)told;
690 time_custom = 1;
691 continue;
692 }
693 continue;
694 }
695
696 continue;
697
698 case 'a':
699 aflg++;
700 continue;
701 case 'A':
702 Aflg++;
703 continue;
704 case 'b':
705 bflg = 1;
706 qflg = 0;
707 continue;
708 case 'B':
709 Bflg = 1;
710 continue;
711 case 'c':
712 uflg = 0;
713 atm = 0;
714 ctm = 0;
715 mtm = 0;
716 crtm = 0;
717 cflg++;
718 continue;
719 case 'C':
720 Cflg = 1;
721 mflg = 0;
722 #ifdef XPG4
723 lflg = 0;
724 #endif
725 continue;
726 case 'd':
727 dflg++;
728 continue;
729 case 'e':
730 eflg++;
731 lflg++;
732 statreq++;
733 Eflg = 0;
734 time_fmt_old = FORMAT_LONG;
735 time_fmt_new = FORMAT_LONG;
736 continue;
737 case 'E':
738 Eflg++;
739 lflg++;
740 statreq++;
741 eflg = 0;
742 time_fmt_old = FORMAT_ISO_FULL;
743 time_fmt_new = FORMAT_ISO_FULL;
744 continue;
745 case 'f':
746 fflg++;
747 continue;
748 case 'F':
749 Fflg++;
750 statreq++;
751 continue;
752 case 'g':
753 gflg++;
754 lflg++;
755 statreq++;
756 continue;
757 case 'h':
758 hflg++;
759 hscale = 1024;
760 continue;
761 case 'H':
762 Hflg++;
763 /* -H and -L are mutually exclusive */
764 Lflg = 0;
765 continue;
766 case 'i':
767 iflg++;
768 continue;
769 case 'k':
770 block_size = 1024;
771 continue;
772 case 'l':
773 lflg++;
774 statreq++;
775 Cflg = 0;
776 xflg = 0;
777 mflg = 0;
778 atflg = 0;
779 continue;
780 case 'L':
781 Lflg++;
782 /* -H and -L are mutually exclusive */
783 Hflg = 0;
784 continue;
785 case 'm':
786 Cflg = 0;
787 mflg = 1;
788 #ifdef XPG4
789 lflg = 0;
790 #endif
791 continue;
792 case 'n':
793 nflg++;
794 lflg++;
795 statreq++;
796 Cflg = 0;
797 xflg = 0;
798 mflg = 0;
799 atflg = 0;
800 continue;
801 case 'o':
802 oflg++;
803 lflg++;
804 statreq++;
805 continue;
806 case 'p':
807 pflg++;
808 statreq++;
809 continue;
810 case 'q':
811 qflg = 1;
812 bflg = 0;
813 continue;
814 case 'r':
815 rflg = -1;
816 continue;
817 case 'R':
818 Rflg++;
819 statreq++;
820 continue;
821 case 's':
822 sflg++;
823 statreq++;
824 continue;
825 case 'S':
826 tflg = 0;
827 Uflg = 0;
828 Sflg++;
829 statreq++;
830 continue;
831 case 't':
832 Sflg = 0;
833 Uflg = 0;
834 tflg++;
835 statreq++;
836 continue;
837 case 'U':
838 Sflg = 0;
839 tflg = 0;
840 Uflg++;
841 continue;
842 case 'u':
843 cflg = 0;
844 atm = 0;
845 ctm = 0;
846 mtm = 0;
847 crtm = 0;
848 uflg++;
849 continue;
850 case 'V':
851 Vflg++;
852 /*FALLTHROUGH*/
853 case 'v':
854 vflg++;
855 #if !defined(XPG4)
856 if (lflg)
857 continue;
858 #endif
859 lflg++;
860 statreq++;
861 Cflg = 0;
862 xflg = 0;
863 mflg = 0;
864 continue;
865 case 'w':
866 wflg++;
867 num_cols = atoi(optarg);
868 continue;
869 case 'x':
870 xflg = 1;
871 Cflg = 1;
872 mflg = 0;
873 #ifdef XPG4
874 lflg = 0;
875 #endif
876 continue;
877 case '1':
878 Cflg = 0;
879 continue;
880 case '@':
881 #if !defined(XPG4)
882 /*
883 * -l has precedence over -@
884 */
885 if (lflg)
886 continue;
887 #endif
888 atflg++;
889 lflg++;
890 statreq++;
891 Cflg = 0;
892 xflg = 0;
893 mflg = 0;
894 continue;
895 case '/':
896 saflg++;
897 if (optarg != NULL) {
898 if (strcmp(optarg, "c") == 0) {
899 copt++;
900 vopt = 0;
901 } else if (strcmp(optarg, "v") == 0) {
902 vopt++;
903 copt = 0;
904 } else
905 opterr++;
906 } else
907 opterr++;
908 lflg++;
909 statreq++;
910 Cflg = 0;
911 xflg = 0;
912 mflg = 0;
913 continue;
914 case '%':
915 tmflg++;
916 if (optarg != NULL) {
917 if (strcmp(optarg, "ctime") == 0) {
918 ctm++;
919 atm = 0;
920 mtm = 0;
921 crtm = 0;
922 } else if (strcmp(optarg, "atime") == 0) {
923 atm++;
924 ctm = 0;
925 mtm = 0;
926 crtm = 0;
927 uflg = 0;
928 cflg = 0;
929 } else if (strcmp(optarg, "mtime") == 0) {
930 mtm++;
931 atm = 0;
932 ctm = 0;
933 crtm = 0;
934 uflg = 0;
935 cflg = 0;
936 } else if (strcmp(optarg, "crtime") == 0) {
937 crtm++;
938 atm = 0;
939 ctm = 0;
940 mtm = 0;
941 uflg = 0;
942 cflg = 0;
943 } else if (strcmp(optarg, "all") == 0) {
944 alltm++;
945 atm = 0;
946 ctm = 0;
947 mtm = 0;
948 crtm = 0;
949 } else
950 opterr++;
951 } else
952 opterr++;
953
954 Sflg = 0;
955 statreq++;
956 mflg = 0;
957 continue;
958 case '?':
959 opterr++;
960 continue;
961 }
962
963 if (opterr) {
964 (void) fprintf(stderr, gettext(
965 "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
966 "%%[atime | crtime | ctime | mtime | all]"
967 " [files]\n"));
968 exit(2);
969 }
970
971 if (fflg) {
972 aflg++;
973 lflg = 0;
974 sflg = 0;
975 tflg = 0;
976 Sflg = 0;
977 statreq = 0;
978 }
979
980 fixedwidth = 2;
981 if (pflg || Fflg)
982 fixedwidth++;
983 if (iflg)
984 fixedwidth += 11;
985 if (sflg)
986 fixedwidth += 5;
987
988 if (lflg) {
989 if (!gflg && !oflg)
990 gflg = oflg = 1;
991 else
992 if (gflg && oflg)
993 gflg = oflg = 0;
994 Cflg = mflg = 0;
995 }
996
997 if (!wflg && (Cflg || mflg)) {
998 char *clptr;
999 if ((clptr = getenv("COLUMNS")) != NULL)
1000 num_cols = atoi(clptr);
1001 #ifdef TERMINFO
1002 else {
1003 if (ioctl(1, TIOCGWINSZ, &win) != -1)
1004 num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
1005 }
1006 #endif
1007 }
1008
1009 /*
1010 * When certain options (-f, or -U and -1, and not -l, etc.) are
1011 * specified, don't cache each dirent as it's read. This 'noflist'
1012 * option is set when there's no need to cache those dirents; instead,
1013 * print them out as they're read.
1014 */
1015 if ((Uflg || fflg) && !Cflg && !lflg && !iflg && statreq == 0)
1016 noflist = 1;
1017
1018 if (num_cols < 20 || num_cols > 1000)
1019 /* assume it is an error */
1020 num_cols = 80;
1021
1022 /* allocate space for flist and the associated */
1023 /* data structures (lbufs) */
1024 maxfils = quantn;
1025 if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
1026 ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
1027 perror("ls");
1028 exit(2);
1029 }
1030 if ((hlbf = malloc(sizeof(*hlbf))) == NULL) {
1031 perror("ls");
1032 exit(2);
1033 }
1034 hlbf[0] = nxtlbf;
1035 if ((amino = (argc-optind)) == 0) {
1036 /*
1037 * case when no names are given
1038 * in ls-command and current
1039 * directory is to be used
1040 */
1041 argv[optind] = dotp;
1042 }
1043
1044 if (colorflg)
1045 ls_color_init();
1046
1047 for (i = 0; i < (amino ? amino : 1); i++) {
1048
1049 /*
1050 * If we are recursing, we need to make sure we don't
1051 * get into an endless loop. To keep track of the inodes
1052 * (actually, just the directories) visited, we
1053 * maintain a directory ancestry list for a file
1054 * hierarchy. As we go deeper into the hierarchy,
1055 * a parent directory passes its directory list
1056 * info (device id, inode number, and a pointer to
1057 * its parent) to each of its children. As we
1058 * process a child that is a directory, we save
1059 * its own personal directory list info. We then
1060 * check to see if the child has already been
1061 * processed by comparing its device id and inode
1062 * number from its own personal directory list info
1063 * to that of each of its ancestors. If there is a
1064 * match, then we know we've detected a cycle.
1065 */
1066 if (Rflg) {
1067 /*
1068 * This is the first parent in this lineage
1069 * (first in a directory hierarchy), so
1070 * this parent's parent doesn't exist. We
1071 * only initialize myinfo when we are
1072 * recursing, otherwise it's not used.
1073 */
1074 if ((myinfo = (struct ditem *)malloc(
1075 sizeof (struct ditem))) == NULL) {
1076 perror("ls");
1077 exit(2);
1078 } else {
1079 myinfo->dev = 0;
1080 myinfo->ino = 0;
1081 myinfo->parent = NULL;
1082 }
1083 }
1084
1085 if (Cflg || mflg) {
1086 width = strcol((unsigned char *)argv[optind]);
1087 if (width > filewidth)
1088 filewidth = width;
1089 }
1090 if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
1091 1, myinfo)) == NULL) {
1092 if (nomocore)
1093 exit(2);
1094 err = 2;
1095 optind++;
1096 continue;
1097 }
1098 ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
1099 ep->lflags |= ISARG;
1100 optind++;
1101 nargs++; /* count good arguments stored in flist */
1102 if (acl_err)
1103 err = 2;
1104 }
1105 colwidth = fixedwidth + filewidth;
1106 if (!Uflg)
1107 qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
1108 (int (*)(const void *, const void *))compar);
1109 for (i = 0; i < nargs; i++) {
1110 if ((flist[i]->ltype == 'd' && dflg == 0) || fflg)
1111 break;
1112 }
1113
1114 pem(&flist[0], &flist[i], 0);
1115 for (; i < nargs; i++) {
1116 pdirectory(flist[i]->ln.namep, Rflg ||
1117 (amino > 1), nargs, 0, flist[i]->ancinfo);
1118 if (nomocore)
1119 exit(2);
1120 /* -R: print subdirectories found */
1121 while (dfirst || cdfirst) {
1122 /* Place direct subdirs on front in right order */
1123 while (cdfirst) {
1124 /* reverse cdfirst onto front of dfirst */
1125 dtemp = cdfirst;
1126 cdfirst = cdfirst -> dc_next;
1127 dtemp -> dc_next = dfirst;
1128 dfirst = dtemp;
1129 }
1130 /* take off first dir on dfirst & print it */
1131 dtemp = dfirst;
1132 dfirst = dfirst->dc_next;
1133 pdirectory(dtemp->dc_name, 1, nargs,
1134 dtemp->cycle_detected, dtemp->myancinfo);
1135 if (nomocore)
1136 exit(2);
1137 free(dtemp->dc_name);
1138 free(dtemp);
1139 }
1140 }
1141
1142 for (i = 0; i < hlbfsz; i ++)
1143 free(hlbf[i]);
1144
1145 free(told);
1146 free(hlbf);
1147 free(flist);
1148 freecachenodes();
1149
1150 return (err);
1151 }
1152
1153 /*
1154 * pdirectory: print the directory name, labelling it if title is
1155 * nonzero, using lp as the place to start reading in the dir.
1156 */
1157 static void
1158 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
1159 {
1160 struct dchain *dp;
1161 struct lbuf *ap;
1162 char *pname;
1163 int j;
1164
1165 filewidth = 0;
1166 curdir = name;
1167 if (title) {
1168 if (!first)
1169 (void) putc('\n', stdout);
1170 pprintf(name, ":");
1171 new_line();
1172 }
1173 /*
1174 * If there was a cycle detected, then notify and don't report
1175 * further.
1176 */
1177 if (cdetect) {
1178 if (lflg || sflg) {
1179 curcol += printf(gettext("total %d"), 0);
1180 new_line();
1181 }
1182 (void) fprintf(stderr, gettext(
1183 "ls: cycle detected for %s\n"), name);
1184 return;
1185 }
1186
1187 nfiles = lp;
1188 rddir(name, myinfo);
1189 if (nomocore || noflist)
1190 return;
1191 if (fflg == 0 && Uflg == 0)
1192 qsort(&flist[lp], (unsigned)(nfiles - lp),
1193 sizeof (struct lbuf *),
1194 (int (*)(const void *, const void *))compar);
1195 if (Rflg) {
1196 for (j = nfiles - 1; j >= lp; j--) {
1197 ap = flist[j];
1198 if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
1199 strcmp(ap->ln.lname, "..")) {
1200 dp = malloc(sizeof (struct dchain));
1201 if (dp == NULL) {
1202 perror("ls");
1203 exit(2);
1204 }
1205 pname = makename(curdir, ap->ln.lname);
1206 if ((dp->dc_name = strdup(pname)) == NULL) {
1207 perror("ls");
1208 exit(2);
1209 }
1210 dp->cycle_detected = ap->cycle;
1211 dp->myancinfo = ap->ancinfo;
1212 dp->dc_next = dfirst;
1213 dfirst = dp;
1214 }
1215 }
1216 }
1217 if (lflg || sflg) {
1218 curcol += printf(gettext("total %llu"), tblocks);
1219 new_line();
1220 }
1221 pem(&flist[lp], &flist[nfiles], lflg||sflg);
1222 }
1223
1224 /*
1225 * pem: print 'em. Print a list of files (e.g. a directory) bounded
1226 * by slp and lp.
1227 */
1228 static void
1229 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
1230 {
1231 long row, nrows, i;
1232 int col, ncols = 1;
1233 struct lbuf **ep;
1234
1235 if (Cflg || mflg) {
1236 if (colwidth <= num_cols) {
1237 ncols = num_cols / colwidth;
1238 }
1239 }
1240
1241 if (ncols == 1 || mflg || xflg || !Cflg) {
1242 for (ep = slp; ep < lp; ep++)
1243 pentry(*ep);
1244 new_line();
1245 return;
1246 }
1247 /* otherwise print -C columns */
1248 if (tot_flag) {
1249 slp--;
1250 row = 1;
1251 }
1252 else
1253 row = 0;
1254
1255 nrows = (lp - slp - 1) / ncols + 1;
1256 for (i = 0; i < nrows; i++, row++) {
1257 for (col = 0; col < ncols; col++) {
1258 ep = slp + (nrows * col) + row;
1259 if (ep < lp)
1260 pentry(*ep);
1261 }
1262 new_line();
1263 }
1264 }
1265
1266 /*
1267 * print one output entry;
1268 * if uid/gid is not found in the appropriate
1269 * file(passwd/group), then print uid/gid instead of
1270 * user/group name;
1271 */
1272 static void
1273 pentry(struct lbuf *ap)
1274 {
1275 struct lbuf *p;
1276 numbuf_t hbuf;
1277 char *dmark = ""; /* Used if -p or -F option active */
1278 char *cp;
1279 char *str;
1280
1281 if (noflist) {
1282 (void) printf("%s\n", (ap->lflags & ISARG) ? ap->ln.namep :
1283 ap->ln.lname);
1284 return;
1285 }
1286
1287 p = ap;
1288 column();
1289 if (iflg) {
1290 if (mflg && !lflg)
1291 curcol += printf("%llu ", (long long)p->lnum);
1292 else
1293 curcol += printf("%10llu ", (long long)p->lnum);
1294 }
1295 if (sflg) {
1296 curcol += printf((mflg && !lflg) ? "%lld " :
1297 (p->lblocks < 10000) ? "%4lld " : "%lld ",
1298 (p->ltype != 'b' && p->ltype != 'c') ?
1299 p->lblocks : 0LL);
1300 }
1301 if (lflg) {
1302 (void) putchar(p->ltype);
1303 curcol++;
1304 pmode(p->lflags);
1305
1306 /* ACL: additional access mode flag */
1307 (void) putchar(p->acl);
1308 curcol++;
1309
1310 curcol += printf("%3lu ", (ulong_t)p->lnl);
1311 if (oflg) {
1312 if (!nflg) {
1313 cp = getname(p->luid);
1314 curcol += printf("%-8s ", cp);
1315 } else
1316 curcol += printf("%-8lu ", (ulong_t)p->luid);
1317 }
1318 if (gflg) {
1319 if (!nflg) {
1320 cp = getgroup(p->lgid);
1321 curcol += printf("%-8s ", cp);
1322 } else
1323 curcol += printf("%-8lu ", (ulong_t)p->lgid);
1324 }
1325 if (p->ltype == 'b' || p->ltype == 'c') {
1326 curcol += printf("%3u, %2u",
1327 (uint_t)major((dev_t)p->lsize),
1328 (uint_t)minor((dev_t)p->lsize));
1329 } else if (hflg && (p->lsize >= hscale)) {
1330 curcol += printf("%7s",
1331 number_to_scaled_string(hbuf, p->lsize, hscale));
1332 } else {
1333 uint64_t bsize = p->lsize / block_size;
1334
1335 /*
1336 * Round up only when using blocks > 1 byte, otherwise
1337 * 'normal' sizes display 1 byte too large.
1338 */
1339 if (p->lsize % block_size != 0)
1340 bsize++;
1341
1342 curcol += printf("%7" PRIu64, bsize);
1343 }
1344 format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
1345 /* format extended system attribute time */
1346 if (tmflg && crtm)
1347 format_attrtime(p);
1348
1349 curcol += printf("%s", time_buf);
1350
1351 }
1352 /*
1353 * prevent both "->" and trailing marks
1354 * from appearing
1355 */
1356
1357 if (pflg && p->ltype == 'd')
1358 dmark = "/";
1359
1360 if (Fflg && !(lflg && p->flinkto)) {
1361 if (p->ltype == 'd')
1362 dmark = "/";
1363 else if (p->ltype == 'D')
1364 dmark = ">";
1365 else if (p->ltype == 'p')
1366 dmark = "|";
1367 else if (p->ltype == 'l')
1368 dmark = "@";
1369 else if (p->ltype == 's')
1370 dmark = "=";
1371 else if (!file_typeflg &&
1372 (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
1373 dmark = "*";
1374 else
1375 dmark = "";
1376 }
1377
1378 if (colorflg)
1379 ls_start_color(p->color);
1380
1381 if (p->lflags & ISARG)
1382 str = p->ln.namep;
1383 else
1384 str = p->ln.lname;
1385
1386 if (qflg || bflg) {
1387 csi_pprintf((unsigned char *)str);
1388
1389 if (lflg && p->flinkto) {
1390 if (colorflg)
1391 ls_end_color();
1392 csi_pprintf((unsigned char *)" -> ");
1393 if (colorflg)
1394 ls_start_color(p->link_color);
1395 csi_pprintf((unsigned char *)p->flinkto);
1396 } else {
1397 csi_pprintf((unsigned char *)dmark);
1398 }
1399 } else {
1400 (void) printf("%s", str);
1401 curcol += strcol((unsigned char *)str);
1402
1403 if (lflg && p->flinkto) {
1404 if (colorflg)
1405 ls_end_color();
1406 str = " -> ";
1407 (void) printf("%s", str);
1408 curcol += strcol((unsigned char *)str);
1409 if (colorflg)
1410 ls_start_color(p->link_color);
1411 (void) printf("%s", p->flinkto);
1412 curcol += strcol((unsigned char *)p->flinkto);
1413 } else {
1414 (void) printf("%s", dmark);
1415 curcol += strcol((unsigned char *)dmark);
1416 }
1417 }
1418
1419 if (colorflg)
1420 ls_end_color();
1421
1422 /* Display extended system attributes */
1423 if (saflg) {
1424 int i;
1425
1426 new_line();
1427 (void) printf(" \t{");
1428 if (p->exttr != NULL) {
1429 int k = 0;
1430 for (i = 0; i < sacnt; i++) {
1431 if (p->exttr[i].name != NULL)
1432 k++;
1433 }
1434 for (i = 0; i < sacnt; i++) {
1435 if (p->exttr[i].name != NULL) {
1436 (void) printf("%s", p->exttr[i].name);
1437 k--;
1438 if (vopt && (k != 0))
1439 (void) printf(",");
1440 }
1441 }
1442 }
1443 (void) printf("}\n");
1444 }
1445 /* Display file timestamps and extended system attribute timestamps */
1446 if (tmflg && alltm) {
1447 new_line();
1448 print_time(p);
1449 new_line();
1450 }
1451 if (vflg) {
1452 new_line();
1453 if (p->aclp) {
1454 acl_printacl(p->aclp, num_cols, Vflg);
1455 }
1456 }
1457 /* Free extended system attribute lists */
1458 if (saflg || tmflg)
1459 free_sysattr(p);
1460 }
1461
1462 /* print various r,w,x permissions */
1463 static void
1464 pmode(mode_t aflag)
1465 {
1466 /* these arrays are declared static to allow initializations */
1467 static int m0[] = { 1, S_IRUSR, 'r', '-' };
1468 static int m1[] = { 1, S_IWUSR, 'w', '-' };
1469 static int m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
1470 'x', S_ISUID, 'S', '-' };
1471 static int m3[] = { 1, S_IRGRP, 'r', '-' };
1472 static int m4[] = { 1, S_IWGRP, 'w', '-' };
1473 static int m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
1474 'x', S_ISGID|LS_NOTREG, 'S',
1475 #ifdef XPG4
1476 S_ISGID, 'L', '-'};
1477 #else
1478 S_ISGID, 'l', '-'};
1479 #endif
1480 static int m6[] = { 1, S_IROTH, 'r', '-' };
1481 static int m7[] = { 1, S_IWOTH, 'w', '-' };
1482 static int m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
1483 'x', S_ISVTX, 'T', '-'};
1484
1485 static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
1486
1487 int **mp;
1488
1489 flags = aflag;
1490 for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
1491 selection(*mp);
1492 }
1493
1494 static void
1495 selection(int *pairp)
1496 {
1497 int n;
1498
1499 n = *pairp++;
1500 while (n-->0) {
1501 if ((flags & *pairp) == *pairp) {
1502 pairp++;
1503 break;
1504 } else {
1505 pairp += 2;
1506 }
1507 }
1508 (void) putchar(*pairp);
1509 curcol++;
1510 }
1511
1512 /*
1513 * column: get to the beginning of the next column.
1514 */
1515 static void
1516 column(void)
1517 {
1518 if (curcol == 0)
1519 return;
1520 if (mflg) {
1521 (void) putc(',', stdout);
1522 curcol++;
1523 if (curcol + colwidth + 2 > num_cols) {
1524 (void) putc('\n', stdout);
1525 curcol = 0;
1526 return;
1527 }
1528 (void) putc(' ', stdout);
1529 curcol++;
1530 return;
1531 }
1532 if (Cflg == 0) {
1533 (void) putc('\n', stdout);
1534 curcol = 0;
1535 return;
1536 }
1537 if ((curcol / colwidth + 2) * colwidth > num_cols) {
1538 (void) putc('\n', stdout);
1539 curcol = 0;
1540 return;
1541 }
1542 do {
1543 (void) putc(' ', stdout);
1544 curcol++;
1545 } while (curcol % colwidth);
1546 }
1547
1548 static void
1549 new_line(void)
1550 {
1551 if (curcol) {
1552 first = 0;
1553 (void) putc('\n', stdout);
1554 curcol = 0;
1555 }
1556 }
1557
1558 /*
1559 * read each filename in directory dir and store its
1560 * status in flist[nfiles]
1561 * use makename() to form pathname dir/filename;
1562 */
1563 static void
1564 rddir(char *dir, struct ditem *myinfo)
1565 {
1566 struct dirent *dentry;
1567 DIR *dirf;
1568 int j;
1569 struct lbuf *ep;
1570 int width;
1571
1572 if ((dirf = opendir(dir)) == NULL) {
1573 (void) fflush(stdout);
1574 perror(dir);
1575 err = 2;
1576 return;
1577 } else {
1578 tblocks = 0;
1579 for (;;) {
1580 errno = 0;
1581 if ((dentry = readdir(dirf)) == NULL)
1582 break;
1583 if (aflg == 0 && dentry->d_name[0] == '.' &&
1584 (Aflg == 0 ||
1585 dentry->d_name[1] == '\0' ||
1586 (dentry->d_name[1] == '.' &&
1587 dentry->d_name[2] == '\0')))
1588 /*
1589 * check for directory items '.', '..',
1590 * and items without valid inode-number;
1591 */
1592 continue;
1593
1594 /* skip entries ending in ~ if -B was given */
1595 if (Bflg &&
1596 dentry->d_name[strlen(dentry->d_name) - 1] == '~')
1597 continue;
1598 if (Cflg || mflg) {
1599 width = strcol((unsigned char *)dentry->d_name);
1600 if (width > filewidth)
1601 filewidth = width;
1602 }
1603 ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
1604 if (ep == NULL) {
1605 if (nomocore)
1606 exit(2);
1607 continue;
1608 } else {
1609 ep->lnum = dentry->d_ino;
1610 for (j = 0; dentry->d_name[j] != '\0'; j++)
1611 ep->ln.lname[j] = dentry->d_name[j];
1612 ep->ln.lname[j] = '\0';
1613
1614 /*
1615 * Since this entry doesn't need to be sorted
1616 * or further processed, print it right away.
1617 */
1618 if (noflist) {
1619 pem(&ep, &ep + 1, 0);
1620 nfiles--;
1621 }
1622 }
1623 }
1624 if (errno) {
1625 int sav_errno = errno;
1626
1627 (void) fprintf(stderr,
1628 gettext("ls: error reading directory %s: %s\n"),
1629 dir, strerror(sav_errno));
1630 }
1631 (void) closedir(dirf);
1632 colwidth = fixedwidth + filewidth;
1633 }
1634 }
1635
1636 /*
1637 * Attaching a link to an inode's ancestors. Search
1638 * through the ancestors to check for cycles (an inode which
1639 * we have already tracked in this inodes ancestry). If a cycle
1640 * is detected, set the exit code and record the fact so that
1641 * it is reported at the right time when printing the directory.
1642 * In addition, set the exit code. Note: If the -a flag was
1643 * specified, we don't want to check for cycles for directories
1644 * ending in '/.' or '/..' unless they were specified on the
1645 * command line.
1646 */
1647 static void
1648 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
1649 int argfl, struct ditem *myparent)
1650 {
1651 size_t file_len;
1652 struct ditem *myinfo;
1653 struct ditem *tptr;
1654
1655 file_len = strlen(file);
1656 if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
1657 NOTPARENTDIR(file, file_len))) {
1658 /*
1659 * Add this inode's ancestry
1660 * info and insert it into the
1661 * ancestry list by pointing
1662 * back to its parent. We save
1663 * it (in rep) with the other info
1664 * we're gathering for this inode.
1665 */
1666 if ((myinfo = malloc(
1667 sizeof (struct ditem))) == NULL) {
1668 perror("ls");
1669 exit(2);
1670 }
1671 myinfo->dev = pstatb->st_dev;
1672 myinfo->ino = pstatb->st_ino;
1673 myinfo->parent = myparent;
1674 rep->ancinfo = myinfo;
1675
1676 /*
1677 * If this node has the same device id and
1678 * inode number of one of its ancestors,
1679 * then we've detected a cycle.
1680 */
1681 if (myparent != NULL) {
1682 for (tptr = myparent; tptr->parent != NULL;
1683 tptr = tptr->parent) {
1684 if ((tptr->dev == pstatb->st_dev) &&
1685 (tptr->ino == pstatb->st_ino)) {
1686 /*
1687 * Cycle detected for this
1688 * directory. Record the fact
1689 * it is a cycle so we don't
1690 * try to process this
1691 * directory as we are
1692 * walking through the
1693 * list of directories.
1694 */
1695 rep->cycle = 1;
1696 err = 2;
1697 break;
1698 }
1699 }
1700 }
1701 }
1702 }
1703
1704 /*
1705 * Do re-calculate the mode for group for ACE_T type of acls.
1706 * This is because, if the server's FS happens to be UFS, supporting
1707 * POSIX ACL's, then it does a special calculation of group mode
1708 * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1709 *
1710 * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1711 * algorithm is used for the group mode calculation only.
1712 * What is modified here from the algorithm is that only the
1713 * entries with flags ACE_GROUP are considered. For each entry
1714 * with ACE_GROUP flag, the first occurance of a specific access
1715 * is checked if it is allowed.
1716 * We are not interested in perms for user and other, as they
1717 * were taken from st_mode value.
1718 * We are not interested in a_who field of ACE, as we need just
1719 * unix mode bits for the group.
1720 */
1721
1722 #define OWNED_GROUP (ACE_GROUP | ACE_IDENTIFIER_GROUP)
1723 #define IS_TYPE_ALLOWED(type) ((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1724
1725 int
1726 grp_mask_to_mode(struct lbuf *p)
1727 {
1728 int mode = 0, seen = 0;
1729 int acecnt;
1730 int flags;
1731 ace_t *ap;
1732 acl_t *acep = p->aclp;
1733
1734 acecnt = acl_cnt(acep);
1735 for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
1736
1737 if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
1738 ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
1739 continue;
1740
1741 if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
1742 continue;
1743
1744 /*
1745 * if it is first group@ or first everyone@
1746 * for each of read, write and execute, then
1747 * that will be the group mode bit.
1748 */
1749 flags = ap->a_flags & ACE_TYPE_FLAGS;
1750 if (flags == OWNED_GROUP || (flags == ACE_IDENTIFIER_GROUP &&
1751 ap->a_who == p->lgid) || flags == ACE_EVERYONE) {
1752 if (ap->a_access_mask & ACE_READ_DATA) {
1753 if (!(seen & S_IRGRP)) {
1754 seen |= S_IRGRP;
1755 if (IS_TYPE_ALLOWED(ap->a_type))
1756 mode |= S_IRGRP;
1757 }
1758 }
1759 if (ap->a_access_mask & ACE_WRITE_DATA) {
1760 if (!(seen & S_IWGRP)) {
1761 seen |= S_IWGRP;
1762 if (IS_TYPE_ALLOWED(ap->a_type))
1763 mode |= S_IWGRP;
1764 }
1765 }
1766 if (ap->a_access_mask & ACE_EXECUTE) {
1767 if (!(seen & S_IXGRP)) {
1768 seen |= S_IXGRP;
1769 if (IS_TYPE_ALLOWED(ap->a_type))
1770 mode |= S_IXGRP;
1771 }
1772 }
1773 }
1774 }
1775 return (mode);
1776 }
1777
1778 /*
1779 * get status of file and recomputes tblocks;
1780 * argfl = 1 if file is a name in ls-command and = 0
1781 * for filename in a directory whose name is an
1782 * argument in the command;
1783 * stores a pointer in flist[nfiles] and
1784 * returns that pointer;
1785 * returns NULL if failed;
1786 */
1787 static struct lbuf *
1788 gstat(char *file, int argfl, struct ditem *myparent)
1789 {
1790 struct stat statb, statb1;
1791 struct lbuf *rep;
1792 char buf[BUFSIZ];
1793 ssize_t cc;
1794 int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
1795 int aclcnt;
1796 int error;
1797 aclent_t *tp;
1798 o_mode_t groupperm, mask;
1799 int grouppermfound, maskfound;
1800
1801 if (nomocore)
1802 return (NULL);
1803
1804 if (nfiles >= maxfils) {
1805 /*
1806 * all flist/lbuf pair assigned files, time to get some
1807 * more space
1808 */
1809 maxfils += quantn;
1810 if (((flist = realloc(flist,
1811 maxfils * sizeof (struct lbuf *))) == NULL) ||
1812 ((nxtlbf = malloc(quantn *
1813 sizeof (struct lbuf))) == NULL)) {
1814 perror("ls");
1815 nomocore = 1;
1816 return (NULL);
1817 }
1818 if ((hlbf = realloc(hlbf, sizeof(*hlbf) * (hlbfsz + 1))) == NULL) {
1819 perror("ls");
1820 nomocore = 1;
1821 return (NULL);
1822 }
1823 hlbf[hlbfsz++] = nxtlbf;
1824 }
1825
1826 /*
1827 * nfiles is reset to nargs for each directory
1828 * that is given as an argument maxn is checked
1829 * to prevent the assignment of an lbuf to a flist entry
1830 * that already has one assigned.
1831 */
1832 if (nfiles >= maxn) {
1833 rep = nxtlbf++;
1834 flist[nfiles++] = rep;
1835 maxn = nfiles;
1836 } else {
1837 rep = flist[nfiles++];
1838 }
1839
1840 /* Clear the lbuf */
1841 (void) memset((void *) rep, 0, sizeof (struct lbuf));
1842
1843 /*
1844 * When noflist is set, none of the extra information about the dirent
1845 * will be printed, so omit remaining initialization of this lbuf
1846 * as well as the stat(2) call.
1847 */
1848 if (!argfl && noflist)
1849 return (rep);
1850
1851 /* Initialize non-zero members */
1852
1853 rep->lat.tv_sec = time(NULL);
1854 rep->lct.tv_sec = time(NULL);
1855 rep->lmt.tv_sec = time(NULL);
1856
1857 if (argfl || statreq) {
1858 int doacl;
1859
1860 if (lflg)
1861 doacl = 1;
1862 else
1863 doacl = 0;
1864
1865 if ((*statf)(file, &statb) < 0) {
1866 if (argfl || errno != ENOENT ||
1867 (Lflg && lstat(file, &statb) == 0)) {
1868 /*
1869 * Avoid race between readdir and lstat.
1870 * Print error message in case of dangling link.
1871 */
1872 perror(file);
1873 err = 2;
1874 }
1875 nfiles--;
1876 return (NULL);
1877 }
1878
1879 /*
1880 * If -H was specified, and the file linked to was
1881 * not a directory, then we need to get the info
1882 * for the symlink itself.
1883 */
1884 if ((Hflg) && (argfl) &&
1885 ((statb.st_mode & S_IFMT) != S_IFDIR)) {
1886 if (lstat(file, &statb) < 0) {
1887 perror(file);
1888 err = 2;
1889 }
1890 }
1891
1892 rep->lnum = statb.st_ino;
1893 rep->lsize = statb.st_size;
1894 rep->lblocks = statb.st_blocks;
1895 if (colorflg)
1896 rep->color = ls_color_find(file, statb.st_mode);
1897
1898 switch (statb.st_mode & S_IFMT) {
1899 case S_IFDIR:
1900 rep->ltype = 'd';
1901 if (Rflg) {
1902 record_ancestry(file, &statb, rep,
1903 argfl, myparent);
1904 }
1905 break;
1906 case S_IFBLK:
1907 rep->ltype = 'b';
1908 rep->lsize = (off_t)statb.st_rdev;
1909 break;
1910 case S_IFCHR:
1911 rep->ltype = 'c';
1912 rep->lsize = (off_t)statb.st_rdev;
1913 break;
1914 case S_IFIFO:
1915 rep->ltype = 'p';
1916 break;
1917 case S_IFSOCK:
1918 rep->ltype = 's';
1919 rep->lsize = 0;
1920 break;
1921 case S_IFLNK:
1922 /* symbolic links may not have ACLs, so elide acl() */
1923 if ((Lflg == 0) || (Hflg == 0) ||
1924 ((Hflg) && (!argfl))) {
1925 doacl = 0;
1926 }
1927 rep->ltype = 'l';
1928 if (lflg || colorflg) {
1929 cc = readlink(file, buf, BUFSIZ);
1930 if (cc < 0)
1931 break;
1932
1933 /*
1934 * follow the symbolic link
1935 * to generate the appropriate
1936 * Fflg marker for the object
1937 * eg, /bin -> /sym/bin/
1938 */
1939 error = 0;
1940 if (Fflg || pflg || colorflg)
1941 error = stat(file, &statb1);
1942
1943 if (colorflg) {
1944 if (error >= 0)
1945 rep->link_color =
1946 ls_color_find(file,
1947 statb1.st_mode);
1948 else
1949 rep->link_color =
1950 lsc_orphan;
1951 }
1952
1953 if ((Fflg || pflg) && error >= 0) {
1954 switch (statb1.st_mode & S_IFMT) {
1955 case S_IFDIR:
1956 buf[cc++] = '/';
1957 break;
1958 case S_IFSOCK:
1959 buf[cc++] = '=';
1960 break;
1961 case S_IFDOOR:
1962 buf[cc++] = '>';
1963 break;
1964 case S_IFIFO:
1965 buf[cc++] = '|';
1966 break;
1967 default:
1968 if ((statb1.st_mode & ~S_IFMT) &
1969 (S_IXUSR|S_IXGRP| S_IXOTH))
1970 buf[cc++] = '*';
1971 break;
1972 }
1973 }
1974 buf[cc] = '\0';
1975 rep->flinkto = strdup(buf);
1976 if (rep->flinkto == NULL) {
1977 perror("ls");
1978 nomocore = 1;
1979 return (NULL);
1980 }
1981 break;
1982 }
1983
1984 /*
1985 * ls /sym behaves differently from ls /sym/
1986 * when /sym is a symbolic link. This is fixed
1987 * when explicit arguments are specified.
1988 */
1989
1990 #ifdef XPG6
1991 /* Do not follow a symlink when -F is specified */
1992 if ((!argfl) || (argfl && Fflg) ||
1993 (stat(file, &statb1) < 0))
1994 #else
1995 /* Follow a symlink when -F is specified */
1996 if (!argfl || stat(file, &statb1) < 0)
1997 #endif /* XPG6 */
1998 break;
1999 if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
2000 statb = statb1;
2001 rep->ltype = 'd';
2002 rep->lsize = statb1.st_size;
2003 if (Rflg) {
2004 record_ancestry(file, &statb, rep,
2005 argfl, myparent);
2006 }
2007 }
2008 break;
2009 case S_IFDOOR:
2010 rep->ltype = 'D';
2011 break;
2012 case S_IFREG:
2013 rep->ltype = '-';
2014 break;
2015 case S_IFPORT:
2016 rep->ltype = 'P';
2017 break;
2018 default:
2019 rep->ltype = '?';
2020 break;
2021 }
2022 rep->lflags = statb.st_mode & ~S_IFMT;
2023
2024 if (!S_ISREG(statb.st_mode))
2025 rep->lflags |= LS_NOTREG;
2026
2027 rep->luid = statb.st_uid;
2028 rep->lgid = statb.st_gid;
2029 rep->lnl = statb.st_nlink;
2030 if (uflg || (tmflg && atm))
2031 rep->lmtime = statb.st_atim;
2032 else if (cflg || (tmflg && ctm))
2033 rep->lmtime = statb.st_ctim;
2034 else
2035 rep->lmtime = statb.st_mtim;
2036 rep->lat = statb.st_atim;
2037 rep->lct = statb.st_ctim;
2038 rep->lmt = statb.st_mtim;
2039
2040 /* ACL: check acl entries count */
2041 if (doacl) {
2042
2043 error = acl_get(file, 0, &rep->aclp);
2044 if (error) {
2045 (void) fprintf(stderr,
2046 gettext("ls: can't read ACL on %s: %s\n"),
2047 file, acl_strerror(error));
2048 rep->acl = ' ';
2049 acl_err++;
2050 return (rep);
2051 }
2052
2053 rep->acl = ' ';
2054
2055 if (rep->aclp &&
2056 ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
2057 rep->acl = '+';
2058 /*
2059 * Special handling for ufs aka aclent_t ACL's
2060 */
2061 if (acl_type(rep->aclp) == ACLENT_T) {
2062 /*
2063 * For files with non-trivial acls, the
2064 * effective group permissions are the
2065 * intersection of the GROUP_OBJ value
2066 * and the CLASS_OBJ (acl mask) value.
2067 * Determine both the GROUP_OBJ and
2068 * CLASS_OBJ for this file and insert
2069 * the logical AND of those two values
2070 * in the group permissions field
2071 * of the lflags value for this file.
2072 */
2073
2074 /*
2075 * Until found in acl list, assume
2076 * maximum permissions for both group
2077 * a nd mask. (Just in case the acl
2078 * lacks either value for some reason.)
2079 */
2080 groupperm = 07;
2081 mask = 07;
2082 grouppermfound = 0;
2083 maskfound = 0;
2084 aclcnt = acl_cnt(rep->aclp);
2085 for (tp =
2086 (aclent_t *)acl_data(rep->aclp);
2087 aclcnt--; tp++) {
2088 if (tp->a_type == GROUP_OBJ) {
2089 groupperm = tp->a_perm;
2090 grouppermfound = 1;
2091 continue;
2092 }
2093 if (tp->a_type == CLASS_OBJ) {
2094 mask = tp->a_perm;
2095 maskfound = 1;
2096 }
2097 if (grouppermfound && maskfound)
2098 break;
2099 }
2100
2101
2102 /* reset all the group bits */
2103 rep->lflags &= ~S_IRWXG;
2104
2105 /*
2106 * Now set them to the logical AND of
2107 * the GROUP_OBJ permissions and the
2108 * acl mask.
2109 */
2110
2111 rep->lflags |= (groupperm & mask) << 3;
2112
2113 } else if (acl_type(rep->aclp) == ACE_T) {
2114 int mode;
2115 mode = grp_mask_to_mode(rep);
2116 rep->lflags &= ~S_IRWXG;
2117 rep->lflags |= mode;
2118 }
2119 }
2120
2121 if (!vflg && !Vflg && rep->aclp) {
2122 acl_free(rep->aclp);
2123 rep->aclp = NULL;
2124 }
2125
2126 if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
2127 rep->acl = '@';
2128
2129 } else
2130 rep->acl = ' ';
2131
2132 /* mask ISARG and other file-type bits */
2133
2134 if (rep->ltype != 'b' && rep->ltype != 'c')
2135 tblocks += rep->lblocks;
2136
2137 /* Get extended system attributes */
2138
2139 if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
2140 (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
2141 int i;
2142
2143 sacnt = attr_count();
2144 /*
2145 * Allocate 'sacnt' size array to hold extended
2146 * system attribute name (verbose) or respective
2147 * symbol represenation (compact).
2148 */
2149 rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
2150 rep);
2151
2152 /* initialize boolean attribute list */
2153 for (i = 0; i < sacnt; i++)
2154 rep->exttr[i].name = NULL;
2155 if (get_sysxattr(file, rep) != 0) {
2156 (void) fprintf(stderr,
2157 gettext("ls:Failed to retrieve "
2158 "extended system attribute from "
2159 "%s\n"), file);
2160 rep->exttr[0].name = xmalloc(2, rep);
2161 (void) strlcpy(rep->exttr[0].name, "?", 2);
2162 }
2163 }
2164 }
2165 return (rep);
2166 }
2167
2168 /*
2169 * returns pathname of the form dir/file;
2170 * dir and file are null-terminated strings.
2171 */
2172 static char *
2173 makename(char *dir, char *file)
2174 {
2175 /*
2176 * PATH_MAX is the maximum length of a path name.
2177 * MAXNAMLEN is the maximum length of any path name component.
2178 * Allocate space for both, plus the '/' in the middle
2179 * and the null character at the end.
2180 * dfile is static as this is returned by makename().
2181 */
2182 static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
2183 char *dp, *fp;
2184
2185 dp = dfile;
2186 fp = dir;
2187 while (*fp)
2188 *dp++ = *fp++;
2189 if (dp > dfile && *(dp - 1) != '/')
2190 *dp++ = '/';
2191 fp = file;
2192 while (*fp)
2193 *dp++ = *fp++;
2194 *dp = '\0';
2195 return (dfile);
2196 }
2197
2198
2199 #include <pwd.h>
2200 #include <grp.h>
2201 #include <utmpx.h>
2202
2203 struct utmpx utmp;
2204
2205 #define NMAX (sizeof (utmp.ut_name))
2206 #define SCPYN(a, b) (void) strncpy(a, b, NMAX)
2207
2208
2209 struct cachenode { /* this struct must be zeroed before using */
2210 struct cachenode *lesschild; /* subtree whose entries < val */
2211 struct cachenode *grtrchild; /* subtree whose entries > val */
2212 long val; /* the uid or gid of this entry */
2213 int initted; /* name has been filled in */
2214 char name[NMAX+1]; /* the string that val maps to */
2215 };
2216 static struct cachenode *names, *groups;
2217
2218 static struct cachenode *
2219 findincache(struct cachenode **head, long val)
2220 {
2221 struct cachenode **parent = head;
2222 struct cachenode *c = *parent;
2223
2224 while (c != NULL) {
2225 if (val == c->val) {
2226 /* found it */
2227 return (c);
2228 } else if (val < c->val) {
2229 parent = &c->lesschild;
2230 c = c->lesschild;
2231 } else {
2232 parent = &c->grtrchild;
2233 c = c->grtrchild;
2234 }
2235 }
2236
2237 /* not in the cache, make a new entry for it */
2238 c = calloc(1, sizeof (struct cachenode));
2239 if (c == NULL) {
2240 perror("ls");
2241 exit(2);
2242 }
2243 *parent = c;
2244 c->val = val;
2245 return (c);
2246 }
2247
2248 void
2249 freecachenode(struct cachenode *node)
2250 {
2251 struct cachenode *current = node;
2252 if (current != NULL) {
2253 struct cachenode *grt = NULL;
2254 struct cachenode *lss = NULL;
2255
2256 if (current->grtrchild != NULL) {
2257 grt = current->grtrchild;
2258 freecachenode(grt);
2259 }
2260 if (current->lesschild != NULL) {
2261 lss = current->lesschild;
2262 freecachenode(lss);
2263 }
2264
2265 free(current);
2266 current = NULL;
2267 }
2268 }
2269
2270 void
2271 freecachenodes(void)
2272 {
2273 freecachenode(groups);
2274 freecachenode(names);
2275 }
2276
2277
2278 /*
2279 * get name from cache, or passwd file for a given uid;
2280 * lastuid is set to uid.
2281 */
2282 static char *
2283 getname(uid_t uid)
2284 {
2285 struct passwd *pwent;
2286 struct cachenode *c;
2287
2288 if ((uid == lastuid) && lastuname)
2289 return (lastuname);
2290
2291 c = findincache(&names, uid);
2292 if (c->initted == 0) {
2293 if ((pwent = getpwuid(uid)) != NULL) {
2294 SCPYN(&c->name[0], pwent->pw_name);
2295 } else {
2296 (void) sprintf(&c->name[0], "%-8u", (int)uid);
2297 }
2298 c->initted = 1;
2299 }
2300 lastuid = uid;
2301 lastuname = &c->name[0];
2302 return (lastuname);
2303 }
2304
2305 /*
2306 * get name from cache, or group file for a given gid;
2307 * lastgid is set to gid.
2308 */
2309 static char *
2310 getgroup(gid_t gid)
2311 {
2312 struct group *grent;
2313 struct cachenode *c;
2314
2315 if ((gid == lastgid) && lastgname)
2316 return (lastgname);
2317
2318 c = findincache(&groups, gid);
2319 if (c->initted == 0) {
2320 if ((grent = getgrgid(gid)) != NULL) {
2321 SCPYN(&c->name[0], grent->gr_name);
2322 } else {
2323 (void) sprintf(&c->name[0], "%-8u", (int)gid);
2324 }
2325 c->initted = 1;
2326 }
2327 lastgid = gid;
2328 lastgname = &c->name[0];
2329 return (lastgname);
2330 }
2331
2332 /* return >0 if item pointed by pp2 should appear first */
2333 static int
2334 compar(struct lbuf **pp1, struct lbuf **pp2)
2335 {
2336 struct lbuf *p1, *p2;
2337
2338 p1 = *pp1;
2339 p2 = *pp2;
2340 if (dflg == 0) {
2341 /*
2342 * compare two names in ls-command one of which is file
2343 * and the other is a directory;
2344 * this portion is not used for comparing files within
2345 * a directory name of ls-command;
2346 */
2347 if (p1->lflags&ISARG && p1->ltype == 'd') {
2348 if (!(p2->lflags&ISARG && p2->ltype == 'd'))
2349 return (1);
2350 } else {
2351 if (p2->lflags&ISARG && p2->ltype == 'd')
2352 return (-1);
2353 }
2354 }
2355 if (tflg) {
2356 if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
2357 return (rflg);
2358 else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
2359 return (-rflg);
2360 /* times are equal to the sec, check nsec */
2361 if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
2362 return (rflg);
2363 else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
2364 return (-rflg);
2365 /* if times are equal, fall through and sort by name */
2366 } else if (Sflg) {
2367 /*
2368 * The size stored in lsize can be either the
2369 * size or the major minor number (in the case of
2370 * block and character special devices). If it's
2371 * a major minor number, then the size is considered
2372 * to be zero and we want to fall through and sort
2373 * by name. In addition, if the size of p2 is equal
2374 * to the size of p1 we want to fall through and
2375 * sort by name.
2376 */
2377 off_t p1size = (p1->ltype == 'b') ||
2378 (p1->ltype == 'c') ? 0 : p1->lsize;
2379 off_t p2size = (p2->ltype == 'b') ||
2380 (p2->ltype == 'c') ? 0 : p2->lsize;
2381 if (p2size > p1size) {
2382 return (rflg);
2383 } else if (p2size < p1size) {
2384 return (-rflg);
2385 }
2386 /* Sizes are equal, fall through and sort by name. */
2387 }
2388 return (rflg * strcoll(
2389 p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
2390 p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
2391 }
2392
2393 static void
2394 pprintf(char *s1, char *s2)
2395 {
2396 csi_pprintf((unsigned char *)s1);
2397 csi_pprintf((unsigned char *)s2);
2398 }
2399
2400 static void
2401 csi_pprintf(unsigned char *s)
2402 {
2403 unsigned char *cp;
2404 char c;
2405 int i;
2406 int c_len;
2407 int p_col;
2408 wchar_t pcode;
2409
2410 if (!qflg && !bflg) {
2411 for (cp = s; *cp != '\0'; cp++) {
2412 (void) putchar(*cp);
2413 curcol++;
2414 }
2415 return;
2416 }
2417
2418 for (cp = s; *cp; ) {
2419 if (isascii(c = *cp)) {
2420 if (!isprint(c)) {
2421 if (qflg) {
2422 c = '?';
2423 } else {
2424 curcol += 3;
2425 (void) putc('\\', stdout);
2426 c = '0' + ((*cp >> 6) & 07);
2427 (void) putc(c, stdout);
2428 c = '0' + ((*cp >> 3) & 07);
2429 (void) putc(c, stdout);
2430 c = '0' + (*cp & 07);
2431 }
2432 }
2433 curcol++;
2434 cp++;
2435 (void) putc(c, stdout);
2436 continue;
2437 }
2438
2439 if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
2440 c_len = 1;
2441 goto not_print;
2442 }
2443
2444 if ((p_col = wcwidth(pcode)) > 0) {
2445 (void) putwchar(pcode);
2446 cp += c_len;
2447 curcol += p_col;
2448 continue;
2449 }
2450
2451 not_print:
2452 for (i = 0; i < c_len; i++) {
2453 if (qflg) {
2454 c = '?';
2455 } else {
2456 curcol += 3;
2457 (void) putc('\\', stdout);
2458 c = '0' + ((*cp >> 6) & 07);
2459 (void) putc(c, stdout);
2460 c = '0' + ((*cp >> 3) & 07);
2461 (void) putc(c, stdout);
2462 c = '0' + (*cp & 07);
2463 }
2464 curcol++;
2465 (void) putc(c, stdout);
2466 cp++;
2467 }
2468 }
2469 }
2470
2471 static int
2472 strcol(unsigned char *s1)
2473 {
2474 int w;
2475 int w_col;
2476 int len;
2477 wchar_t wc;
2478
2479 w = 0;
2480 while (*s1) {
2481 if (isascii(*s1)) {
2482 w++;
2483 s1++;
2484 continue;
2485 }
2486
2487 if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
2488 w++;
2489 s1++;
2490 continue;
2491 }
2492
2493 if ((w_col = wcwidth(wc)) < 0)
2494 w_col = len;
2495 s1 += len;
2496 w += w_col;
2497 }
2498 return (w);
2499 }
2500
2501 /*
2502 * Convert an unsigned long long to a string representation and place the
2503 * result in the caller-supplied buffer.
2504 *
2505 * The number provided is a size in bytes. The number is first
2506 * converted to an integral multiple of 'scale' bytes. This new
2507 * number is then scaled down until it is small enough to be in a good
2508 * human readable format, i.e. in the range 0 thru scale-1. If the
2509 * number used to derive the final number is not a multiple of scale, and
2510 * the final number has only a single significant digit, we compute
2511 * tenths of units to provide a second significant digit.
2512 *
2513 * The value "(unsigned long long)-1" is a special case and is always
2514 * converted to "-1".
2515 *
2516 * A pointer to the caller-supplied buffer is returned.
2517 */
2518 static char *
2519 number_to_scaled_string(
2520 numbuf_t buf, /* put the result here */
2521 unsigned long long number, /* convert this number */
2522 long scale)
2523 {
2524 unsigned long long save;
2525 /* Measurement: kilo, mega, giga, tera, peta, exa */
2526 char *uom = "KMGTPE";
2527
2528 if ((long long)number == (long long)-1) {
2529 (void) strlcpy(buf, "-1", sizeof (numbuf_t));
2530 return (buf);
2531 }
2532
2533 save = number;
2534 number = number / scale;
2535
2536 /*
2537 * Now we have number as a count of scale units.
2538 * If no further scaling is necessary, we round up as appropriate.
2539 *
2540 * The largest value number could have had entering the routine is
2541 * 16 Exabytes, so running off the end of the uom array should
2542 * never happen. We check for that, though, as a guard against
2543 * a breakdown elsewhere in the algorithm.
2544 */
2545 if (number < (unsigned long long)scale) {
2546 if ((save % scale) >= (unsigned long long)(scale / 2)) {
2547 if (++number == (unsigned long long)scale) {
2548 uom++;
2549 number = 1;
2550 }
2551 }
2552 } else {
2553 while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
2554 uom++; /* next unit of measurement */
2555 save = number;
2556 /*
2557 * If we're over half way to the next unit of
2558 * 'scale' bytes (which means we should round
2559 * up), then adding half of 'scale' prior to
2560 * the division will push us into that next
2561 * unit of scale when we perform the division
2562 */
2563 number = (number + (scale / 2)) / scale;
2564 }
2565 }
2566
2567 /* check if we should output a decimal place after the point */
2568 if ((save / scale) < 10) {
2569 /* snprintf() will round for us */
2570 float fnum = (float)save / scale;
2571 (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
2572 fnum, *uom);
2573 } else {
2574 (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
2575 number, *uom);
2576 }
2577 return (buf);
2578 }
2579
2580 /* Get extended system attributes and set the display */
2581
2582 int
2583 get_sysxattr(char *fname, struct lbuf *rep)
2584 {
2585 boolean_t value;
2586 data_type_t type;
2587 int error;
2588 char *name;
2589 int i;
2590
2591 if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
2592 &response)) != 0) {
2593 perror("ls:getattrat");
2594 return (error);
2595 }
2596
2597 /*
2598 * Allocate 'sacnt' size array to hold extended timestamp
2599 * system attributes and initialize the array.
2600 */
2601 rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
2602 for (i = 0; i < sacnt; i++) {
2603 rep->extm[i].stm = 0;
2604 rep->extm[i].nstm = 0;
2605 rep->extm[i].name = NULL;
2606 }
2607 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
2608 name = nvpair_name(pair);
2609 type = nvpair_type(pair);
2610 if (type == DATA_TYPE_BOOLEAN_VALUE) {
2611 error = nvpair_value_boolean_value(pair, &value);
2612 if (error) {
2613 (void) fprintf(stderr,
2614 gettext("nvpair_value_boolean_value "
2615 "failed: error = %d\n"), error);
2616 continue;
2617 }
2618 if (name != NULL)
2619 set_sysattrb_display(name, value, rep);
2620 continue;
2621 } else if (type == DATA_TYPE_UINT64_ARRAY) {
2622 if (name != NULL)
2623 set_sysattrtm_display(name, rep);
2624 continue;
2625 }
2626 }
2627 nvlist_free(response);
2628 return (0);
2629 }
2630
2631 /* Set extended system attribute boolean display */
2632
2633 void
2634 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
2635 {
2636 f_attr_t fattr;
2637 const char *opt;
2638 size_t len;
2639
2640 fattr = name_to_attr(name);
2641 if (fattr != F_ATTR_INVAL && fattr < sacnt) {
2642 if (vopt) {
2643 len = strlen(name);
2644 if (val) {
2645 rep->exttr[fattr].name = xmalloc(len + 1, rep);
2646 (void) strlcpy(rep->exttr[fattr].name, name,
2647 len + 1);
2648 } else {
2649 rep->exttr[fattr].name = xmalloc(len + 3, rep);
2650 (void) snprintf(rep->exttr[fattr].name, len + 3,
2651 "no%s", name);
2652 }
2653 } else {
2654 opt = attr_to_option(fattr);
2655 if (opt != NULL) {
2656 len = strlen(opt);
2657 rep->exttr[fattr].name = xmalloc(len + 1, rep);
2658 if (val)
2659 (void) strlcpy(rep->exttr[fattr].name,
2660 opt, len + 1);
2661 else
2662 (void) strlcpy(rep->exttr[fattr].name,
2663 "-", len + 1);
2664 }
2665 }
2666 }
2667 }
2668
2669 /* Set extended system attribute timestamp display */
2670
2671 void
2672 set_sysattrtm_display(char *name, struct lbuf *rep)
2673 {
2674 uint_t nelem;
2675 uint64_t *value;
2676 int i;
2677 size_t len;
2678
2679 if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
2680 if (value != NULL) {
2681 len = strlen(name);
2682 i = 0;
2683 while (rep->extm[i].stm != 0 && i < sacnt)
2684 i++;
2685 rep->extm[i].stm = value[0];
2686 rep->extm[i].nstm = value[1];
2687 rep->extm[i].name = xmalloc(len + 1, rep);
2688 (void) strlcpy(rep->extm[i].name, name, len + 1);
2689 }
2690 }
2691 }
2692
2693 void
2694 format_time(time_t sec, time_t nsec)
2695 {
2696 const char *fstr = time_fmt_new;
2697 char fmt_buf[FMTSIZE];
2698
2699 if (Eflg) {
2700 (void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
2701 (void) strftime(time_buf, sizeof (time_buf), fmt_buf,
2702 localtime(&sec));
2703 return;
2704 }
2705
2706 if (sec < year || sec > now)
2707 fstr = time_fmt_old;
2708
2709 /* if a custom time was specified, shouldn't be localized */
2710 (void) strftime(time_buf, sizeof (time_buf),
2711 (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
2712 localtime(&sec));
2713 }
2714
2715 void
2716 format_attrtime(struct lbuf *p)
2717 {
2718 int tmattr = 0;
2719 int i;
2720
2721 if (p->extm != NULL) {
2722 for (i = 0; i < sacnt; i++) {
2723 if (p->extm[i].name != NULL) {
2724 tmattr = 1;
2725 break;
2726 }
2727 }
2728 }
2729
2730 if (tmattr) {
2731 const char *old_save = time_fmt_old;
2732 const char *new_save = time_fmt_new;
2733
2734 /* Eflg always sets format to FORMAT_ISO_FULL */
2735 if (!Eflg && !time_custom) {
2736 time_fmt_old = FORMAT_OLD;
2737 time_fmt_new = FORMAT_NEW;
2738 }
2739
2740 format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
2741
2742 time_fmt_old = old_save;
2743 time_fmt_new = new_save;
2744 }
2745 }
2746
2747 void
2748 print_time(struct lbuf *p)
2749 {
2750 const char *old_save = time_fmt_old;
2751 const char *new_save = time_fmt_new;
2752
2753 int i = 0;
2754
2755 if (!Eflg) {
2756 time_fmt_old = FORMAT_LONG;
2757 time_fmt_new = FORMAT_LONG;
2758 }
2759
2760 new_line();
2761 format_time(p->lat.tv_sec, p->lat.tv_nsec);
2762 (void) printf(" timestamp: atime %s\n", time_buf);
2763 format_time(p->lct.tv_sec, p->lct.tv_nsec);
2764 (void) printf(" timestamp: ctime %s\n", time_buf);
2765 format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
2766 (void) printf(" timestamp: mtime %s\n", time_buf);
2767 if (p->extm != NULL) {
2768 while (p->extm[i].nstm != 0 && i < sacnt) {
2769 format_time(p->extm[i].stm, p->extm[i].nstm);
2770 if (p->extm[i].name != NULL) {
2771 (void) printf(" timestamp:"
2772 " %s %s\n",
2773 p->extm[i].name, time_buf);
2774 }
2775 i++;
2776 }
2777 }
2778
2779 time_fmt_old = old_save;
2780 time_fmt_new = new_save;
2781 }
2782
2783 /*
2784 * Check if color definition applies to entry, returns 1 if yes, 0 if no
2785 */
2786 static int
2787 color_match(const char *fname, mode_t mode, ls_color_t *color)
2788 {
2789 switch (color->ftype) {
2790 case LS_PAT:
2791 {
2792 size_t fname_len, sfx_len;
2793
2794 fname_len = strlen(fname);
2795 sfx_len = strlen(color->sfx);
2796 if (sfx_len > fname_len)
2797 return (0);
2798
2799 if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
2800 return (1);
2801 else
2802 return (0);
2803 }
2804
2805 case LS_NORMAL:
2806 return (1);
2807
2808 case LS_FILE:
2809 return (S_ISREG(mode));
2810
2811 case LS_DIR:
2812 return (S_ISDIR(mode));
2813
2814 case LS_LINK:
2815 return (S_ISLNK(mode));
2816
2817 case LS_FIFO:
2818 return (S_ISFIFO(mode));
2819
2820 case LS_SOCK:
2821 return (S_ISSOCK(mode));
2822
2823 case LS_DOOR:
2824 return (S_ISDOOR(mode));
2825
2826 case LS_BLK:
2827 return (S_ISBLK(mode));
2828
2829 case LS_CHR:
2830 return (S_ISCHR(mode));
2831
2832 case LS_PORT:
2833 return (S_ISPORT(mode));
2834
2835 case LS_ORPHAN:
2836 /* this is tested for by gstat */
2837 return (0);
2838
2839 case LS_SETUID:
2840 return (!S_ISLNK(mode) && (mode & S_ISUID));
2841
2842 case LS_SETGID:
2843 return (!S_ISLNK(mode) && (mode & S_ISGID));
2844
2845 case LS_STICKY_OTHER_WRITABLE:
2846 return (!S_ISLNK(mode) && (mode & (S_IWOTH|S_ISVTX)));
2847
2848 case LS_OTHER_WRITABLE:
2849 return (!S_ISLNK(mode) && (mode & S_IWOTH));
2850
2851 case LS_STICKY:
2852 return (!S_ISLNK(mode) && (mode & S_ISVTX));
2853
2854 case LS_EXEC:
2855 return (!S_ISLNK(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH)));
2856 }
2857
2858 return (0);
2859 }
2860
2861 static void
2862 dump_color(ls_color_t *c)
2863 {
2864 if (c == NULL)
2865 return;
2866
2867 (void) printf("\n\ttype: ");
2868 switch (c->ftype) {
2869 case LS_NORMAL:
2870 (void) printf("LS_NORMAL");
2871 break;
2872 case LS_FILE:
2873 (void) printf("LS_FILE");
2874 break;
2875 case LS_EXEC:
2876 (void) printf("LS_EXEC");
2877 break;
2878 case LS_DIR:
2879 (void) printf("LS_DIR");
2880 break;
2881 case LS_LINK:
2882 (void) printf("LS_LINK");
2883 break;
2884
2885 case LS_FIFO:
2886 (void) printf("LS_FIFO");
2887 break;
2888
2889 case LS_SOCK:
2890 (void) printf("LS_SOCK");
2891 break;
2892
2893 case LS_DOOR:
2894 (void) printf("LS_DOOR");
2895 break;
2896
2897 case LS_BLK:
2898 (void) printf("LS_BLK");
2899 break;
2900
2901 case LS_CHR:
2902 (void) printf("LS_CHR");
2903 break;
2904
2905 case LS_PORT:
2906 (void) printf("LS_PORT");
2907 break;
2908
2909 case LS_STICKY:
2910 (void) printf("LS_STICKY");
2911 break;
2912
2913 case LS_ORPHAN:
2914 (void) printf("LS_ORPHAN");
2915 break;
2916
2917 case LS_SETGID:
2918 (void) printf("LS_SETGID");
2919 break;
2920
2921 case LS_SETUID:
2922 (void) printf("LS_SETUID");
2923 break;
2924
2925 case LS_OTHER_WRITABLE:
2926 (void) printf("LS_OTHER_WRITABLE");
2927 break;
2928
2929 case LS_STICKY_OTHER_WRITABLE:
2930 (void) printf("LS_STICKY_OTHER_WRITABLE");
2931 break;
2932
2933 case LS_PAT:
2934 (void) printf("LS_PAT\n");
2935 (void) printf("\tpattern: %s", c->sfx);
2936 break;
2937 }
2938 (void) printf("\n");
2939 (void) printf("\tattr: %d\n", c->attr);
2940 (void) printf("\tfg: %d\n", c->fg);
2941 (void) printf("\tbg: %d\n", c->bg);
2942 (void) printf("\t");
2943 }
2944
2945 static ls_color_t *
2946 ls_color_find(const char *fname, mode_t mode)
2947 {
2948 int i;
2949
2950 /*
2951 * Colors are sorted from most general lsc_colors[0] to most specific
2952 * lsc_colors[lsc_ncolors - 1] by ls_color_init(). Start search with
2953 * most specific color rule and work towards most general.
2954 */
2955 for (i = lsc_ncolors - 1; i >= 0; --i)
2956 if (color_match(fname, mode, &lsc_colors[i]))
2957 return (&lsc_colors[i]);
2958
2959 return (NULL);
2960 }
2961
2962 static void
2963 ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
2964 long int p5, long int p6, long int p7, long int p8, long int p9)
2965 {
2966 char *s;
2967
2968 if (str == NULL)
2969 return;
2970
2971 s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
2972
2973 if (s != NULL)
2974 (void) putp(s);
2975 }
2976
2977 static void
2978 ls_start_color(ls_color_t *c)
2979 {
2980 if (c == NULL)
2981 return;
2982
2983 if (lsc_debug)
2984 lsc_match = c;
2985
2986 if (c->attr & LSA_BOLD)
2987 ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2988 if (c->attr & LSA_UNDERSCORE)
2989 ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2990 if (c->attr & LSA_BLINK)
2991 ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2992 if (c->attr & LSA_REVERSE)
2993 ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2994 if (c->attr & LSA_CONCEALED)
2995 ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2996 if (c->attr == LSA_NONE)
2997 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2998
2999 if (c->fg != -1)
3000 ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
3001 if (c->bg != -1)
3002 ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
3003 }
3004
3005 static void
3006 ls_end_color()
3007 {
3008 ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3009 if (lsc_debug)
3010 dump_color(lsc_match);
3011 }
3012
3013 static void
3014 new_color_entry(char *colorstr)
3015 {
3016 static const struct {
3017 const char *s;
3018 ls_cftype_t stype;
3019 } type_map[] = {
3020 { "no", LS_NORMAL },
3021 { "fi", LS_FILE },
3022 { "di", LS_DIR },
3023 { "ln", LS_LINK },
3024 { "pi", LS_FIFO },
3025 { "so", LS_SOCK },
3026 { "do", LS_DOOR },
3027 { "bd", LS_BLK },
3028 { "cd", LS_CHR },
3029 { "or", LS_ORPHAN },
3030 { "su", LS_SETUID },
3031 { "sg", LS_SETGID },
3032 { "tw", LS_STICKY_OTHER_WRITABLE },
3033 { "ow", LS_OTHER_WRITABLE },
3034 { "st", LS_STICKY },
3035 { "ex", LS_EXEC },
3036 { "po", LS_PORT },
3037 { NULL, LS_NORMAL }
3038 };
3039
3040 char *p, *lasts;
3041 int i;
3042 int color, attr;
3043
3044 p = strtok_r(colorstr, "=", &lasts);
3045 if (p == NULL) {
3046 colorflg = 0;
3047 return;
3048 }
3049
3050 if (p[0] == '*') {
3051 lsc_colors[lsc_ncolors].ftype = LS_PAT;
3052 /* don't include the * in the suffix */
3053 if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
3054 colorflg = 0;
3055 return;
3056 }
3057 } else {
3058 lsc_colors[lsc_ncolors].sfx = NULL;
3059
3060 for (i = 0; type_map[i].s != NULL; ++i) {
3061 if (strncmp(type_map[i].s, p, 2) == 0)
3062 break;
3063 }
3064
3065 /* ignore unknown file types */
3066 if (type_map[i].s == NULL)
3067 return;
3068
3069 lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
3070 }
3071
3072 attr = LSA_NONE;
3073 lsc_colors[lsc_ncolors].fg = -1;
3074 lsc_colors[lsc_ncolors].bg = -1;
3075 for (p = strtok_r(NULL, ";", &lasts); p != NULL;
3076 p = strtok_r(NULL, ";", &lasts)) {
3077 color = strtol(p, NULL, 10);
3078
3079 if (color < 10) {
3080 switch (color) {
3081 case 0:
3082 attr = LSA_NONE;
3083 continue;
3084 case 1:
3085 attr |= LSA_BOLD;
3086 continue;
3087 case 4:
3088 attr |= LSA_UNDERSCORE;
3089 continue;
3090 case 5:
3091 attr |= LSA_BLINK;
3092 continue;
3093 case 7:
3094 attr |= LSA_REVERSE;
3095 continue;
3096 case 8:
3097 attr |= LSA_CONCEALED;
3098 continue;
3099 default:
3100 continue;
3101 }
3102 }
3103
3104 if (color < 40)
3105 lsc_colors[lsc_ncolors].fg = color - 30;
3106 else
3107 lsc_colors[lsc_ncolors].bg = color - 40;
3108 }
3109
3110 lsc_colors[lsc_ncolors].attr = attr;
3111 ++lsc_ncolors;
3112 }
3113
3114 static int
3115 ls_color_compare(const void *p1, const void *p2)
3116 {
3117 const ls_color_t *c1 = (const ls_color_t *)p1;
3118 const ls_color_t *c2 = (const ls_color_t *)p2;
3119
3120 int ret = c1->ftype - c2->ftype;
3121
3122 if (ret != 0)
3123 return (ret);
3124
3125 if (c1->ftype != LS_PAT)
3126 return (ret);
3127
3128 return (strcmp(c1->sfx, c2->sfx));
3129 }
3130
3131 static void
3132 ls_color_init()
3133 {
3134 static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3135 ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3136 ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3137 ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3138 ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3139 ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3140 ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3141 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3142 ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3143 ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3144 ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3145
3146 char *colorstr;
3147 char *p, *lasts;
3148 size_t color_sz;
3149 int termret;
3150 int i;
3151
3152 (void) setupterm(NULL, 1, &termret);
3153 if (termret != 1)
3154 return;
3155
3156 if ((p = getenv("LS_COLORS")) == NULL)
3157 p = default_colorstr;
3158 colorstr = strdup(p);
3159 if (colorstr == NULL)
3160 return;
3161
3162 /*
3163 * Determine the size of lsc_colors. color_sz can be > lsc_ncolors
3164 * if there are invalid entries passed in the string (they are ignored)
3165 */
3166 color_sz = 1;
3167 for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
3168 p = strchr(++p, ':'))
3169 ++color_sz;
3170
3171 lsc_colors = calloc(color_sz, sizeof (ls_color_t));
3172 if (lsc_colors == NULL) {
3173 free(colorstr);
3174 return;
3175 }
3176
3177 for (p = strtok_r(colorstr, ":", &lasts);
3178 p != NULL && lsc_ncolors < color_sz;
3179 p = strtok_r(NULL, ":", &lasts))
3180 new_color_entry(p);
3181
3182 qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
3183 ls_color_compare);
3184
3185 for (i = 0; i < lsc_ncolors; ++i)
3186 if (lsc_colors[i].ftype == LS_ORPHAN) {
3187 lsc_orphan = &lsc_colors[i];
3188 break;
3189 }
3190
3191 if ((lsc_bold = tigetstr("bold")) == (char *)-1)
3192 lsc_bold = NULL;
3193
3194 if ((lsc_underline = tigetstr("smul")) == (char *)-1)
3195 lsc_underline = NULL;
3196
3197 if ((lsc_blink = tigetstr("blink")) == (char *)-1)
3198 lsc_blink = NULL;
3199
3200 if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
3201 lsc_reverse = NULL;
3202
3203 if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
3204 lsc_concealed = NULL;
3205
3206 if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
3207 lsc_none = NULL;
3208
3209 if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
3210 lsc_setfg = NULL;
3211
3212 if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
3213 lsc_setbg = NULL;
3214
3215 if (getenv("_LS_COLOR_DEBUG") != NULL) {
3216 int i;
3217
3218 lsc_debug = 1;
3219 for (i = 0; i < lsc_ncolors; ++i)
3220 dump_color(&lsc_colors[i]);
3221 }
3222
3223 free(colorstr);
3224 }
3225
3226 /* Free extended system attribute lists */
3227
3228 void
3229 free_sysattr(struct lbuf *p)
3230 {
3231 int i;
3232
3233 if (p->exttr != NULL) {
3234 for (i = 0; i < sacnt; i++) {
3235 if (p->exttr[i].name != NULL)
3236 free(p->exttr[i].name);
3237 }
3238 free(p->exttr);
3239 }
3240 if (p->extm != NULL) {
3241 for (i = 0; i < sacnt; i++) {
3242 if (p->extm[i].name != NULL)
3243 free(p->extm[i].name);
3244 }
3245 free(p->extm);
3246 }
3247 }
3248
3249 /* Allocate extended system attribute list */
3250
3251 void *
3252 xmalloc(size_t size, struct lbuf *p)
3253 {
3254 if ((p = malloc(size)) == NULL) {
3255 perror("ls");
3256 free_sysattr(p);
3257 nvlist_free(response);
3258 exit(2);
3259 }
3260 return (p);
3261 }