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>
68 static int printaname(const FTSENT *, u_long, u_long);
69 static void printdev(size_t, dev_t);
70 static void printlink(const FTSENT *);
71 static void printtime(const char *, time_t);
72 static int printtype(u_int);
73 static void printsize(const char *, size_t, off_t);
75 static void endcolor(int);
76 static int colortype(mode_t);
78 static void aclmode(char *, const FTSENT *);
80 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
83 /* Most of these are taken from <sys/stat.h> */
85 C_DIR, /* directory */
86 C_LNK, /* symbolic link */
89 C_EXEC, /* executable */
90 C_BLK, /* block special */
91 C_CHR, /* character special */
92 C_SUID, /* setuid executable */
93 C_SGID, /* setgid executable */
94 C_WSDIR, /* directory writeble to others, with sticky
96 C_WDIR, /* directory writeble to others, without
98 C_NUMCOLORS /* just a place-holder */
101 static const char *defcolors = "exfxcxdxbxegedabagacad";
103 /* colors for file types */
107 } colors[C_NUMCOLORS];
110 static size_t padding_for_month[12];
111 static size_t month_max_size = 0;
114 printscol(const DISPLAY *dp)
118 xo_open_list("entry");
119 for (p = dp->list; p; p = p->fts_link) {
122 xo_open_instance("entry");
123 (void)printaname(p, dp->s_inode, dp->s_block);
124 xo_close_instance("entry");
127 xo_close_list("entry");
131 * print name in current style
134 printname(const char *field, const char *name)
137 char *s = getname(name);
140 snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field);
141 rc = xo_emit(fmt, s);
151 case 0: return (nl_langinfo(ABMON_1));
152 case 1: return (nl_langinfo(ABMON_2));
153 case 2: return (nl_langinfo(ABMON_3));
154 case 3: return (nl_langinfo(ABMON_4));
155 case 4: return (nl_langinfo(ABMON_5));
156 case 5: return (nl_langinfo(ABMON_6));
157 case 6: return (nl_langinfo(ABMON_7));
158 case 7: return (nl_langinfo(ABMON_8));
159 case 8: return (nl_langinfo(ABMON_9));
160 case 9: return (nl_langinfo(ABMON_10));
161 case 10: return (nl_langinfo(ABMON_11));
162 case 11: return (nl_langinfo(ABMON_12));
165 /* should never happen */
170 mbswidth(const char *month)
173 size_t width, donelen, clen, w;
176 while ((clen = mbrtowc(&wc, month + donelen, MB_LEN_MAX, NULL)) != 0) {
177 if (clen == (size_t)-1 || clen == (size_t)-2)
180 if ((w = wcwidth(wc)) == (size_t)-1)
189 compute_abbreviated_month_size(void)
193 size_t months_width[12];
195 for (i = 0; i < 12; i++) {
196 width = mbswidth(get_abmon(i));
197 if (width == (size_t)-1) {
201 months_width[i] = width;
202 if (width > month_max_size)
203 month_max_size = width;
206 for (i = 0; i < 12; i++)
207 padding_for_month[i] = month_max_size - months_width[i];
211 * print name in current style
214 getname(const char *name)
216 if (f_octal || f_octal_escape)
217 return get_octal(name);
219 return get_printable(name);
225 printlong(const DISPLAY *dp)
232 int color_printed = 0;
235 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
236 (f_longform || f_size)) {
237 xo_emit("{L:total} {:total-blocks/%lu}\n",
238 howmany(dp->btotal, blocksize));
241 xo_open_list("entry");
242 for (p = dp->list; p; p = p->fts_link) {
246 xo_open_instance("entry");
248 name = getname(p->fts_name);
250 xo_emit("{ke:name/%hs}", name);
252 xo_emit("{t:inode/%*ju} ",
253 dp->s_inode, (uintmax_t)sp->st_ino);
255 xo_emit("{t:blocks/%*jd} ",
256 dp->s_block, howmany(sp->st_blocks, blocksize));
257 strmode(sp->st_mode, buf);
260 xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS);
262 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*ju} {td:user/%-*s}{e:user/%ju} {td:group/%-*s}{e:group/%ju} ",
263 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)sp->st_nlink,
264 dp->s_user, np->user, (uintmax_t)sp->st_uid, dp->s_group, np->group, (uintmax_t)sp->st_gid);
266 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*ju} {t:user/%-*s} {t:group/%-*s} ",
267 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)sp->st_nlink,
268 dp->s_user, np->user, dp->s_group, np->group);
270 if (S_ISBLK(sp->st_mode))
271 asprintf(&type, "block");
272 if (S_ISCHR(sp->st_mode))
273 asprintf(&type, "character");
274 if (S_ISDIR(sp->st_mode))
275 asprintf(&type, "directory");
276 if (S_ISFIFO(sp->st_mode))
277 asprintf(&type, "fifo");
278 if (S_ISLNK(sp->st_mode))
279 asprintf(&type, "symlink");
280 if (S_ISREG(sp->st_mode))
281 asprintf(&type, "regular");
282 if (S_ISSOCK(sp->st_mode))
283 asprintf(&type, "socket");
284 if (S_ISWHT(sp->st_mode))
285 asprintf(&type, "whiteout");
286 xo_emit("{e:type/%s}", type);
289 xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags);
291 xo_emit("{t:label/%-*s} ", dp->s_label, np->label);
292 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
293 printdev(dp->s_size, sp->st_rdev);
295 printsize("size", dp->s_size, sp->st_size);
297 printtime("access-time", sp->st_atime);
298 else if (f_birthtime)
299 printtime("birth-time", sp->st_birthtime);
300 else if (f_statustime)
301 printtime("change-time", sp->st_ctime);
303 printtime("modify-time", sp->st_mtime);
306 color_printed = colortype(sp->st_mode);
310 xo_emit("{dk:name/%hs}", name);
315 if (f_color && color_printed)
319 (void)printtype(sp->st_mode);
320 if (S_ISLNK(sp->st_mode))
322 xo_close_instance("entry");
325 xo_close_list("entry");
329 printstream(const DISPLAY *dp)
334 xo_open_list("entry");
335 for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
336 if (p->fts_number == NO_PRINT)
338 /* XXX strlen does not take octal escapes into account. */
339 if (strlen(p->fts_name) + chcnt +
340 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
344 xo_open_instance("file");
345 chcnt += printaname(p, dp->s_inode, dp->s_block);
346 xo_close_instance("file");
352 xo_close_list("entry");
358 printcol(const DISPLAY *dp)
360 static FTSENT **array;
361 static int lastentries = -1;
382 * Have to do random access in the linked list -- build a table
385 if (dp->entries > lastentries) {
387 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
391 lastentries = dp->entries;
394 for (p = dp->list, num = 0; p; p = p->fts_link)
395 if (p->fts_number != NO_PRINT)
398 colwidth = dp->maxlen;
400 colwidth += dp->s_inode + 1;
402 colwidth += dp->s_block + 1;
406 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
407 if (termwidth < 2 * colwidth) {
411 numcols = termwidth / colwidth;
412 numrows = num / numcols;
416 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
417 (f_longform || f_size)) {
418 xo_emit("{L:total} {:total-blocks/%lu}\n",
419 howmany(dp->btotal, blocksize));
422 xo_open_list("entry");
424 for (row = 0; row < numrows; ++row) {
428 for (col = 0, chcnt = 0; col < numcols; ++col) {
429 xo_open_instance("entry");
430 chcnt += printaname(array[base], dp->s_inode,
432 xo_close_instance("entry");
439 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
441 if (f_sortacross && col + 1 >= numcols)
443 xo_emit(f_notabs ? " " : "\t");
450 xo_close_list("entry");
454 * print [inode] [size] name
455 * return # of characters printed, no trailing characters.
458 printaname(const FTSENT *p, u_long inodefield, u_long sizefield)
463 int color_printed = 0;
469 chcnt += xo_emit("{t:inode/%*ju} ",
470 (int)inodefield, (uintmax_t)sp->st_ino);
472 chcnt += xo_emit("{t:size/%*jd} ",
473 (int)sizefield, howmany(sp->st_blocks, blocksize));
476 color_printed = colortype(sp->st_mode);
478 chcnt += printname("name", p->fts_name);
480 if (f_color && color_printed)
484 chcnt += printtype(sp->st_mode);
489 * Print device special file major and minor numbers.
492 printdev(size_t width, dev_t dev)
494 xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev);
498 ls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm)
500 char *posb, nfmt[BUFSIZ];
501 const char *format = fmt;
504 if ((posb = strstr(fmt, "%b")) != NULL) {
505 if (month_max_size == 0) {
506 compute_abbreviated_month_size();
508 if (month_max_size > 0) {
509 snprintf(nfmt, sizeof(nfmt), "%.*s%s%*s%s",
510 (int)(posb - fmt), fmt,
511 get_abmon(tm->tm_mon),
512 (int)padding_for_month[tm->tm_mon],
518 ret = strftime(str, len, format, tm);
523 printtime(const char *field, time_t ftime)
527 static time_t now = 0;
529 static int d_first = -1;
532 d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
536 #define SIXMONTHS ((365 / 2) * 86400)
537 if (f_timeformat) /* user specified format */
538 format = f_timeformat;
540 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
541 format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
542 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
543 /* mmm dd hh:mm || dd mmm hh:mm */
544 format = d_first ? "%e %b %R" : "%b %e %R";
546 /* mmm dd yyyy || dd mmm yyyy */
547 format = d_first ? "%e %b %Y" : "%b %e %Y";
548 ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime));
550 snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field);
551 xo_attr("value", "%ld", (long) ftime);
552 xo_emit(fmt, longstring);
553 snprintf(fmt, sizeof(fmt), "{en:%s/%%ld}", field);
554 xo_emit(fmt, (long) ftime);
558 printtype(u_int mode)
562 if ((mode & S_IFMT) == S_IFDIR) {
563 xo_emit("{D:\\/}{e:type/directory}");
569 switch (mode & S_IFMT) {
571 xo_emit("{D:/\\/}{e:type/directory}");
574 xo_emit("{D:|}{e:type/fifo}");
577 xo_emit("{D:@}{e:type/link}");
580 xo_emit("{D:=}{e:type/socket}");
583 xo_emit("{D:%%}{e:type/whiteout}");
588 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
589 xo_emit("{D:*}{e:executable/}");
599 xo_emit("{D:/%c}", c);
608 (void)write(STDOUT_FILENO, &tmp, 1);
618 tputs(enter_bold, 1, putch);
620 if (colors[c].num[0] != -1) {
621 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
623 tputs(ansiseq, 1, putch);
625 if (colors[c].num[1] != -1) {
626 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
628 tputs(ansiseq, 1, putch);
635 tputs(ansi_coloff, 1, sig ? writech : putch);
636 tputs(attrs_off, 1, sig ? writech : putch);
640 colortype(mode_t mode)
642 switch (mode & S_IFMT) {
669 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
672 else if (mode & S_ISGID)
682 parsecolors(const char *cs)
688 short legacy_warn = 0;
691 cs = ""; /* LSCOLORS not set */
693 for (i = 0; i < (int)C_NUMCOLORS; i++) {
696 if (len <= 2 * (size_t)i) {
697 c[0] = defcolors[2 * i];
698 c[1] = defcolors[2 * i + 1];
701 c[1] = cs[2 * i + 1];
703 for (j = 0; j < 2; j++) {
704 /* Legacy colours used 0-7 */
705 if (c[j] >= '0' && c[j] <= '7') {
706 colors[i].num[j] = c[j] - '0';
708 xo_warnx("LSCOLORS should use "
709 "characters a-h instead of 0-9 ("
710 "see the manual page)");
713 } else if (c[j] >= 'a' && c[j] <= 'h')
714 colors[i].num[j] = c[j] - 'a';
715 else if (c[j] >= 'A' && c[j] <= 'H') {
716 colors[i].num[j] = c[j] - 'A';
718 } else if (tolower((unsigned char)c[j]) == 'x')
719 colors[i].num[j] = -1;
721 xo_warnx("invalid character '%c' in LSCOLORS"
723 colors[i].num[j] = -1;
734 (void)signal(sig, SIG_DFL);
735 (void)kill(getpid(), sig);
741 printlink(const FTSENT *p)
744 char name[MAXPATHLEN + 1];
745 char path[MAXPATHLEN + 1];
747 if (p->fts_level == FTS_ROOTLEVEL)
748 (void)snprintf(name, sizeof(name), "%s", p->fts_name);
750 (void)snprintf(name, sizeof(name),
751 "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
752 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
753 xo_error("\nls: %s: %s\n", name, strerror(errno));
758 (void)printname("target", path);
762 printsize(const char *field, size_t width, off_t bytes)
768 * Reserve one space before the size and allocate room for
771 char buf[HUMANVALSTR_LEN - 1 + 1];
773 humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
774 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
775 snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width);
776 xo_attr("value", "%jd", (intmax_t) bytes);
778 } else { /* with commas */
779 /* This format assignment needed to work round gcc bug. */
780 snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ",
781 field, (int) width, f_thousands ? "'" : "");
782 xo_emit(fmt, (intmax_t) bytes);
787 * Add a + after the standard rwxrwxrwx mode if the file has an
788 * ACL. strmode() reserves space at the end of the string.
791 aclmode(char *buf, const FTSENT *p)
793 char name[MAXPATHLEN + 1];
795 static dev_t previous_dev = NODEV;
796 static int supports_acls = -1;
797 static int type = ACL_TYPE_ACCESS;
801 * XXX: ACLs are not supported on whiteouts and device files
804 if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
805 S_ISWHT(p->fts_statp->st_mode))
808 if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
811 if (p->fts_level == FTS_ROOTLEVEL)
812 snprintf(name, sizeof(name), "%s", p->fts_name);
814 snprintf(name, sizeof(name), "%s/%s",
815 p->fts_parent->fts_accpath, p->fts_name);
817 if (previous_dev != p->fts_statp->st_dev) {
818 previous_dev = p->fts_statp->st_dev;
821 ret = lpathconf(name, _PC_ACL_NFS4);
823 type = ACL_TYPE_NFS4;
825 } else if (ret < 0 && errno != EINVAL) {
829 if (supports_acls == 0) {
830 ret = lpathconf(name, _PC_ACL_EXTENDED);
832 type = ACL_TYPE_ACCESS;
834 } else if (ret < 0 && errno != EINVAL) {
840 if (supports_acls == 0)
842 facl = acl_get_link_np(name, type);
847 if (acl_is_trivial_np(facl, &trivial)) {