2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94";
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
41 #include <sys/param.h>
66 static int printaname(const FTSENT *, u_long, u_long);
67 static void printdev(size_t, dev_t);
68 static void printlink(const FTSENT *);
69 static void printtime(const char *, time_t);
70 static int printtype(u_int);
71 static void printsize(const char *, size_t, off_t);
73 static void endcolor(int);
74 static int colortype(mode_t);
76 static void aclmode(char *, const FTSENT *);
78 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
81 /* Most of these are taken from <sys/stat.h> */
83 C_DIR, /* directory */
84 C_LNK, /* symbolic link */
87 C_EXEC, /* executable */
88 C_BLK, /* block special */
89 C_CHR, /* character special */
90 C_SUID, /* setuid executable */
91 C_SGID, /* setgid executable */
92 C_WSDIR, /* directory writeble to others, with sticky
94 C_WDIR, /* directory writeble to others, without
96 C_NUMCOLORS /* just a place-holder */
99 static const char *defcolors = "exfxcxdxbxegedabagacad";
101 /* colors for file types */
105 } colors[C_NUMCOLORS];
109 printscol(const DISPLAY *dp)
113 xo_open_list("entry");
114 for (p = dp->list; p; p = p->fts_link) {
117 xo_open_instance("entry");
118 (void)printaname(p, dp->s_inode, dp->s_block);
119 xo_close_instance("entry");
122 xo_close_list("entry");
126 * print name in current style
129 printname(const char *field, const char *name)
132 char *s = getname(name);
135 snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field);
136 rc = xo_emit(fmt, s);
142 * print name in current style
145 getname(const char *name)
147 if (f_octal || f_octal_escape)
148 return get_octal(name);
150 return get_printable(name);
156 printlong(const DISPLAY *dp)
163 int color_printed = 0;
166 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
167 (f_longform || f_size)) {
168 xo_emit("{L:total} {:total-blocks/%lu}\n",
169 howmany(dp->btotal, blocksize));
172 xo_open_list("entry");
173 for (p = dp->list; p; p = p->fts_link) {
177 xo_open_instance("entry");
179 name = getname(p->fts_name);
181 xo_emit("{ke:name/%hs}", name);
183 xo_emit("{t:inode/%*ju} ",
184 dp->s_inode, (uintmax_t)sp->st_ino);
186 xo_emit("{t:blocks/%*jd} ",
187 dp->s_block, howmany(sp->st_blocks, blocksize));
188 strmode(sp->st_mode, buf);
191 xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS);
193 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*u} {td:user/%-*s}{e:user/%ju} {td:group/%-*s}{e:group/%ju} ",
194 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, sp->st_nlink,
195 dp->s_user, np->user, (uintmax_t)sp->st_uid, dp->s_group, np->group, (uintmax_t)sp->st_gid);
197 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*u} {t:user/%-*s} {t:group/%-*s} ",
198 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, sp->st_nlink,
199 dp->s_user, np->user, dp->s_group, np->group);
201 if (S_ISBLK(sp->st_mode))
202 asprintf(&type, "block");
203 if (S_ISCHR(sp->st_mode))
204 asprintf(&type, "character");
205 if (S_ISDIR(sp->st_mode))
206 asprintf(&type, "directory");
207 if (S_ISFIFO(sp->st_mode))
208 asprintf(&type, "fifo");
209 if (S_ISLNK(sp->st_mode))
210 asprintf(&type, "symlink");
211 if (S_ISREG(sp->st_mode))
212 asprintf(&type, "regular");
213 if (S_ISSOCK(sp->st_mode))
214 asprintf(&type, "socket");
215 if (S_ISWHT(sp->st_mode))
216 asprintf(&type, "whiteout");
217 xo_emit("{e:type/%s}", type);
220 xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags);
222 xo_emit("{t:label/%-*s} ", dp->s_label, np->label);
223 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
224 printdev(dp->s_size, sp->st_rdev);
226 printsize("size", dp->s_size, sp->st_size);
228 printtime("access-time", sp->st_atime);
229 else if (f_birthtime)
230 printtime("birth-time", sp->st_birthtime);
231 else if (f_statustime)
232 printtime("change-time", sp->st_ctime);
234 printtime("modify-time", sp->st_mtime);
237 color_printed = colortype(sp->st_mode);
241 xo_emit("{dk:name/%hs}", name);
246 if (f_color && color_printed)
250 (void)printtype(sp->st_mode);
251 if (S_ISLNK(sp->st_mode))
253 xo_close_instance("entry");
256 xo_close_list("entry");
260 printstream(const DISPLAY *dp)
265 xo_open_list("entry");
266 for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
267 if (p->fts_number == NO_PRINT)
269 /* XXX strlen does not take octal escapes into account. */
270 if (strlen(p->fts_name) + chcnt +
271 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
275 xo_open_instance("file");
276 chcnt += printaname(p, dp->s_inode, dp->s_block);
277 xo_close_instance("file");
283 xo_close_list("entry");
289 printcol(const DISPLAY *dp)
291 static FTSENT **array;
292 static int lastentries = -1;
313 * Have to do random access in the linked list -- build a table
316 if (dp->entries > lastentries) {
318 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
322 lastentries = dp->entries;
325 for (p = dp->list, num = 0; p; p = p->fts_link)
326 if (p->fts_number != NO_PRINT)
329 colwidth = dp->maxlen;
331 colwidth += dp->s_inode + 1;
333 colwidth += dp->s_block + 1;
337 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
338 if (termwidth < 2 * colwidth) {
342 numcols = termwidth / colwidth;
343 numrows = num / numcols;
347 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
348 (f_longform || f_size)) {
349 xo_emit("{L:total} {:total-blocks/%lu}\n",
350 howmany(dp->btotal, blocksize));
353 xo_open_list("entry");
355 for (row = 0; row < numrows; ++row) {
359 for (col = 0, chcnt = 0; col < numcols; ++col) {
360 xo_open_instance("entry");
361 chcnt += printaname(array[base], dp->s_inode,
363 xo_close_instance("entry");
370 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
372 if (f_sortacross && col + 1 >= numcols)
374 xo_emit(f_notabs ? " " : "\t");
381 xo_close_list("entry");
385 * print [inode] [size] name
386 * return # of characters printed, no trailing characters.
389 printaname(const FTSENT *p, u_long inodefield, u_long sizefield)
394 int color_printed = 0;
400 chcnt += xo_emit("{t:inode/%*ju} ",
401 (int)inodefield, (uintmax_t)sp->st_ino);
403 chcnt += xo_emit("{t:size/%*jd} ",
404 (int)sizefield, howmany(sp->st_blocks, blocksize));
407 color_printed = colortype(sp->st_mode);
409 chcnt += printname("name", p->fts_name);
411 if (f_color && color_printed)
415 chcnt += printtype(sp->st_mode);
420 * Print device special file major and minor numbers.
423 printdev(size_t width, dev_t dev)
425 xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev);
429 printtime(const char *field, time_t ftime)
433 static time_t now = 0;
435 static int d_first = -1;
438 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
442 #define SIXMONTHS ((365 / 2) * 86400)
443 if (f_timeformat) /* user specified format */
444 format = f_timeformat;
446 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
447 format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
448 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
449 /* mmm dd hh:mm || dd mmm hh:mm */
450 format = d_first ? "%e %b %R" : "%b %e %R";
452 /* mmm dd yyyy || dd mmm yyyy */
453 format = d_first ? "%e %b %Y" : "%b %e %Y";
454 strftime(longstring, sizeof(longstring), format, localtime(&ftime));
456 snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field);
457 xo_attr("value", "%ld", (long) ftime);
458 xo_emit(fmt, longstring);
459 snprintf(fmt, sizeof(fmt), "{en:%s/%%ld}", field);
460 xo_emit(fmt, (long) ftime);
464 printtype(u_int mode)
468 if ((mode & S_IFMT) == S_IFDIR) {
469 xo_emit("{D:\\/}{e:type/directory}");
475 switch (mode & S_IFMT) {
477 xo_emit("{D:/\\/}{e:type/directory}");
480 xo_emit("{D:|}{e:type/fifo}");
483 xo_emit("{D:@}{e:type/link}");
486 xo_emit("{D:=}{e:type/socket}");
489 xo_emit("{D:%%}{e:type/whiteout}");
494 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
495 xo_emit("{D:*}{e:executable/}");
505 xo_emit("{D:/%c}", c);
514 (void)write(STDOUT_FILENO, &tmp, 1);
524 tputs(enter_bold, 1, putch);
526 if (colors[c].num[0] != -1) {
527 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
529 tputs(ansiseq, 1, putch);
531 if (colors[c].num[1] != -1) {
532 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
534 tputs(ansiseq, 1, putch);
541 tputs(ansi_coloff, 1, sig ? writech : putch);
542 tputs(attrs_off, 1, sig ? writech : putch);
546 colortype(mode_t mode)
548 switch (mode & S_IFMT) {
575 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
578 else if (mode & S_ISGID)
588 parsecolors(const char *cs)
594 short legacy_warn = 0;
597 cs = ""; /* LSCOLORS not set */
599 for (i = 0; i < (int)C_NUMCOLORS; i++) {
602 if (len <= 2 * (size_t)i) {
603 c[0] = defcolors[2 * i];
604 c[1] = defcolors[2 * i + 1];
607 c[1] = cs[2 * i + 1];
609 for (j = 0; j < 2; j++) {
610 /* Legacy colours used 0-7 */
611 if (c[j] >= '0' && c[j] <= '7') {
612 colors[i].num[j] = c[j] - '0';
614 xo_warnx("LSCOLORS should use "
615 "characters a-h instead of 0-9 ("
616 "see the manual page)");
619 } else if (c[j] >= 'a' && c[j] <= 'h')
620 colors[i].num[j] = c[j] - 'a';
621 else if (c[j] >= 'A' && c[j] <= 'H') {
622 colors[i].num[j] = c[j] - 'A';
624 } else if (tolower((unsigned char)c[j]) == 'x')
625 colors[i].num[j] = -1;
627 xo_warnx("invalid character '%c' in LSCOLORS"
629 colors[i].num[j] = -1;
640 (void)signal(sig, SIG_DFL);
641 (void)kill(getpid(), sig);
647 printlink(const FTSENT *p)
650 char name[MAXPATHLEN + 1];
651 char path[MAXPATHLEN + 1];
653 if (p->fts_level == FTS_ROOTLEVEL)
654 (void)snprintf(name, sizeof(name), "%s", p->fts_name);
656 (void)snprintf(name, sizeof(name),
657 "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
658 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
659 xo_error("\nls: %s: %s\n", name, strerror(errno));
664 (void)printname("target", path);
668 printsize(const char *field, size_t width, off_t bytes)
674 * Reserve one space before the size and allocate room for
677 char buf[HUMANVALSTR_LEN - 1 + 1];
679 humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
680 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
681 snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width);
682 xo_attr("value", "%jd", (intmax_t) bytes);
684 } else { /* with commas */
685 /* This format assignment needed to work round gcc bug. */
686 snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ",
687 field, (int) width, f_thousands ? "'" : "");
688 xo_emit(fmt, (intmax_t) bytes);
693 * Add a + after the standard rwxrwxrwx mode if the file has an
694 * ACL. strmode() reserves space at the end of the string.
697 aclmode(char *buf, const FTSENT *p)
699 char name[MAXPATHLEN + 1];
701 static dev_t previous_dev = NODEV;
702 static int supports_acls = -1;
703 static int type = ACL_TYPE_ACCESS;
707 * XXX: ACLs are not supported on whiteouts and device files
710 if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
711 S_ISWHT(p->fts_statp->st_mode))
714 if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
717 if (p->fts_level == FTS_ROOTLEVEL)
718 snprintf(name, sizeof(name), "%s", p->fts_name);
720 snprintf(name, sizeof(name), "%s/%s",
721 p->fts_parent->fts_accpath, p->fts_name);
723 if (previous_dev != p->fts_statp->st_dev) {
724 previous_dev = p->fts_statp->st_dev;
727 ret = lpathconf(name, _PC_ACL_NFS4);
729 type = ACL_TYPE_NFS4;
731 } else if (ret < 0 && errno != EINVAL) {
735 if (supports_acls == 0) {
736 ret = lpathconf(name, _PC_ACL_EXTENDED);
738 type = ACL_TYPE_ACCESS;
740 } else if (ret < 0 && errno != EINVAL) {
746 if (supports_acls == 0)
748 facl = acl_get_link_np(name, type);
753 if (acl_is_trivial_np(facl, &trivial)) {