print.c revision 284198
1139969Simp/*-
21556Srgrimes * Copyright (c) 1989, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Michael Fischbein.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
3390153Smarkm#if 0
341556Srgrimes#ifndef lint
3527967Sstevestatic char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
3690153Smarkm#endif /* not lint */
3727967Ssteve#endif
3899109Sobrien#include <sys/cdefs.h>
3999109Sobrien__FBSDID("$FreeBSD: head/bin/ls/print.c 284198 2015-06-10 01:27:38Z marcel $");
401556Srgrimes
411556Srgrimes#include <sys/param.h>
421556Srgrimes#include <sys/stat.h>
43106371Stjr#include <sys/acl.h>
441556Srgrimes
451556Srgrimes#include <err.h>
461556Srgrimes#include <errno.h>
471556Srgrimes#include <fts.h>
4874566Sache#include <langinfo.h>
49129719Spjd#include <libutil.h>
501556Srgrimes#include <stdio.h>
51225847Sed#include <stdint.h>
521556Srgrimes#include <stdlib.h>
531556Srgrimes#include <string.h>
5491212Sbde#include <time.h>
551556Srgrimes#include <unistd.h>
5661294Sache#ifdef COLORLS
5761294Sache#include <ctype.h>
5861294Sache#include <termcap.h>
5961294Sache#include <signal.h>
6061294Sache#endif
61284198Smarcel#include <libxo/xo.h>
621556Srgrimes
631556Srgrimes#include "ls.h"
641556Srgrimes#include "extern.h"
651556Srgrimes
66114583Smarkmstatic int	printaname(const FTSENT *, u_long, u_long);
67202945Sjhstatic void	printdev(size_t, dev_t);
68105780Smarkmstatic void	printlink(const FTSENT *);
69284198Smarcelstatic void	printtime(const char *, time_t);
7090110Simpstatic int	printtype(u_int);
71284198Smarcelstatic void	printsize(const char *, size_t, off_t);
7261321Sache#ifdef COLORLS
7390110Simpstatic void	endcolor(int);
7490110Simpstatic int	colortype(mode_t);
7561321Sache#endif
76196712Straszstatic void	aclmode(char *, const FTSENT *);
771556Srgrimes
781556Srgrimes#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
791556Srgrimes
8061268Sjoe#ifdef COLORLS
8161178Sjoe/* Most of these are taken from <sys/stat.h> */
8261178Sjoetypedef enum Colors {
8388602Sjoe	C_DIR,			/* directory */
8488602Sjoe	C_LNK,			/* symbolic link */
8588602Sjoe	C_SOCK,			/* socket */
8688602Sjoe	C_FIFO,			/* pipe */
8788602Sjoe	C_EXEC,			/* executable */
8888602Sjoe	C_BLK,			/* block special */
8988602Sjoe	C_CHR,			/* character special */
9088602Sjoe	C_SUID,			/* setuid executable */
9188602Sjoe	C_SGID,			/* setgid executable */
9288602Sjoe	C_WSDIR,		/* directory writeble to others, with sticky
9388602Sjoe				 * bit */
9488602Sjoe	C_WDIR,			/* directory writeble to others, without
9588602Sjoe				 * sticky bit */
9688602Sjoe	C_NUMCOLORS		/* just a place-holder */
9788586Sjoe} Colors;
9861178Sjoe
9990150Smarkmstatic const char *defcolors = "exfxcxdxbxegedabagacad";
10061178Sjoe
10188583Sjoe/* colors for file types */
10288583Sjoestatic struct {
10388586Sjoe	int	num[2];
10488586Sjoe	int	bold;
10588583Sjoe} colors[C_NUMCOLORS];
10661268Sjoe#endif
10761178Sjoe
1081556Srgrimesvoid
109114583Smarkmprintscol(const DISPLAY *dp)
1101556Srgrimes{
11188594Sjoe	FTSENT *p;
1121556Srgrimes
113284198Smarcel	xo_open_list("entry");
1141556Srgrimes	for (p = dp->list; p; p = p->fts_link) {
1151556Srgrimes		if (IS_NOPRINT(p))
1161556Srgrimes			continue;
117284198Smarcel		xo_open_instance("entry");
1181556Srgrimes		(void)printaname(p, dp->s_inode, dp->s_block);
119284198Smarcel		xo_close_instance("entry");
120284198Smarcel		xo_emit("\n");
1211556Srgrimes	}
122284198Smarcel	xo_close_list("entry");
1231556Srgrimes}
1241556Srgrimes
12562597Sassar/*
12662597Sassar * print name in current style
12762597Sassar */
128105390Stjrint
129284198Smarcelprintname(const char *field, const char *name)
13062597Sassar{
131284198Smarcel	char fmt[BUFSIZ];
132284198Smarcel	char *s = getname(name);
133284198Smarcel	int rc;
134284198Smarcel
135284198Smarcel	snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field);
136284198Smarcel	rc = xo_emit(fmt, s);
137284198Smarcel	free(s);
138284198Smarcel	return rc;
139284198Smarcel}
140284198Smarcel
141284198Smarcel/*
142284198Smarcel * print name in current style
143284198Smarcel */
144284198Smarcelchar *
145284198Smarcelgetname(const char *name)
146284198Smarcel{
14762597Sassar	if (f_octal || f_octal_escape)
148284198Smarcel		return get_octal(name);
14962597Sassar	else if (f_nonprint)
150284198Smarcel		return get_printable(name);
15162597Sassar	else
152284198Smarcel		return strdup(name);
15362597Sassar}
15462597Sassar
1551556Srgrimesvoid
156114583Smarkmprintlong(const DISPLAY *dp)
1571556Srgrimes{
1581556Srgrimes	struct stat *sp;
15988594Sjoe	FTSENT *p;
16088594Sjoe	NAMES *np;
16188594Sjoe	char buf[20];
16261292Sache#ifdef COLORLS
16388594Sjoe	int color_printed = 0;
16461292Sache#endif
1651556Srgrimes
166130236Sdas	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
167130236Sdas	    (f_longform || f_size)) {
168284198Smarcel		xo_emit("{L:total} {:total-blocks/%lu}\n",
169284198Smarcel			howmany(dp->btotal, blocksize));
170130236Sdas	}
1711556Srgrimes
172284198Smarcel	xo_open_list("entry");
1731556Srgrimes	for (p = dp->list; p; p = p->fts_link) {
174284198Smarcel		char *name;
1751556Srgrimes		if (IS_NOPRINT(p))
1761556Srgrimes			continue;
177284198Smarcel		xo_open_instance("entry");
1781556Srgrimes		sp = p->fts_statp;
179284198Smarcel		name = getname(p->fts_name);
180284198Smarcel		if (name)
181284198Smarcel		    xo_emit("{ke:name}", name);
1821556Srgrimes		if (f_inode)
183284198Smarcel			xo_emit("{:inode/%*ju} ",
184241014Smdf			    dp->s_inode, (uintmax_t)sp->st_ino);
1851556Srgrimes		if (f_size)
186284198Smarcel			xo_emit("{:blocks/%*jd} ",
1871556Srgrimes			    dp->s_block, howmany(sp->st_blocks, blocksize));
18890150Smarkm		strmode(sp->st_mode, buf);
189196712Strasz		aclmode(buf, p);
1901556Srgrimes		np = p->fts_pointer;
191284198Smarcel		xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS);
192284198Smarcel		xo_emit("{t:mode/%s} {:links/%*u} {:user/%-*s}  {:group/%-*s}  ",
193284198Smarcel			buf, dp->s_nlink, sp->st_nlink,
194284198Smarcel			dp->s_user, np->user, dp->s_group, np->group);
1951556Srgrimes		if (f_flags)
196284198Smarcel			xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags);
197105832Srwatson		if (f_label)
198284198Smarcel			xo_emit("{:label/%-*s} ", dp->s_label, np->label);
1991556Srgrimes		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
200202945Sjh			printdev(dp->s_size, sp->st_rdev);
2011556Srgrimes		else
202284198Smarcel			printsize("size", dp->s_size, sp->st_size);
2031556Srgrimes		if (f_accesstime)
204284198Smarcel			printtime("access-time", sp->st_atime);
205157098Sjhb		else if (f_birthtime)
206284198Smarcel			printtime("birth-time", sp->st_birthtime);
2071556Srgrimes		else if (f_statustime)
208284198Smarcel			printtime("change-time", sp->st_ctime);
2091556Srgrimes		else
210284198Smarcel			printtime("modify-time", sp->st_mtime);
21161268Sjoe#ifdef COLORLS
21261178Sjoe		if (f_color)
21361291Sache			color_printed = colortype(sp->st_mode);
21461268Sjoe#endif
215284198Smarcel
216284198Smarcel		if (name) {
217284198Smarcel		    xo_emit("{dk:name}", name);
218284198Smarcel		    free(name);
219284198Smarcel		}
220284198Smarcel
22161268Sjoe#ifdef COLORLS
22261291Sache		if (f_color && color_printed)
22361321Sache			endcolor(0);
22461268Sjoe#endif
2251556Srgrimes		if (f_type)
2261556Srgrimes			(void)printtype(sp->st_mode);
2271556Srgrimes		if (S_ISLNK(sp->st_mode))
2281556Srgrimes			printlink(p);
229284198Smarcel		xo_close_instance("entry");
230284198Smarcel		xo_emit("\n");
2311556Srgrimes	}
232284198Smarcel	xo_close_list("entry");
2331556Srgrimes}
2341556Srgrimes
2351556Srgrimesvoid
236114583Smarkmprintstream(const DISPLAY *dp)
23796892Stjr{
23896892Stjr	FTSENT *p;
23996892Stjr	int chcnt;
24096892Stjr
24196892Stjr	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
24296892Stjr		if (p->fts_number == NO_PRINT)
24396892Stjr			continue;
244128823Stjr		/* XXX strlen does not take octal escapes into account. */
24596892Stjr		if (strlen(p->fts_name) + chcnt +
24696892Stjr		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
247284198Smarcel			xo_emit("\n");
24896892Stjr			chcnt = 0;
24996892Stjr		}
25096892Stjr		chcnt += printaname(p, dp->s_inode, dp->s_block);
25196892Stjr		if (p->fts_link) {
252284198Smarcel			xo_emit(", ");
25396892Stjr			chcnt += 2;
25496892Stjr		}
25596892Stjr	}
25696892Stjr	if (chcnt)
257284198Smarcel		xo_emit("\n");
25896892Stjr}
259177907Sgrog
26096892Stjrvoid
261114583Smarkmprintcol(const DISPLAY *dp)
2621556Srgrimes{
2631556Srgrimes	static FTSENT **array;
2641556Srgrimes	static int lastentries = -1;
26588594Sjoe	FTSENT *p;
266121124Stjr	FTSENT **narray;
26788594Sjoe	int base;
26888594Sjoe	int chcnt;
26988594Sjoe	int cnt;
27088594Sjoe	int col;
27188594Sjoe	int colwidth;
27288594Sjoe	int endcol;
27388594Sjoe	int num;
27488594Sjoe	int numcols;
27588594Sjoe	int numrows;
27688594Sjoe	int row;
27788594Sjoe	int tabwidth;
2781556Srgrimes
27937932Shoek	if (f_notabs)
28037932Shoek		tabwidth = 1;
28137932Shoek	else
28237932Shoek		tabwidth = 8;
28337932Shoek
2841556Srgrimes	/*
2851556Srgrimes	 * Have to do random access in the linked list -- build a table
2861556Srgrimes	 * of pointers.
2871556Srgrimes	 */
2881556Srgrimes	if (dp->entries > lastentries) {
289121124Stjr		if ((narray =
2901556Srgrimes		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
291284198Smarcel			xo_warn(NULL);
2921556Srgrimes			printscol(dp);
293121124Stjr			return;
2941556Srgrimes		}
295121124Stjr		lastentries = dp->entries;
296121124Stjr		array = narray;
2971556Srgrimes	}
2981556Srgrimes	for (p = dp->list, num = 0; p; p = p->fts_link)
2991556Srgrimes		if (p->fts_number != NO_PRINT)
3001556Srgrimes			array[num++] = p;
3011556Srgrimes
3021556Srgrimes	colwidth = dp->maxlen;
3031556Srgrimes	if (f_inode)
3041556Srgrimes		colwidth += dp->s_inode + 1;
3051556Srgrimes	if (f_size)
3061556Srgrimes		colwidth += dp->s_block + 1;
3071556Srgrimes	if (f_type)
3081556Srgrimes		colwidth += 1;
3091556Srgrimes
31037932Shoek	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
3111556Srgrimes	if (termwidth < 2 * colwidth) {
3121556Srgrimes		printscol(dp);
3131556Srgrimes		return;
3141556Srgrimes	}
3151556Srgrimes	numcols = termwidth / colwidth;
3161556Srgrimes	numrows = num / numcols;
3171556Srgrimes	if (num % numcols)
3181556Srgrimes		++numrows;
3191556Srgrimes
320130236Sdas	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
321130236Sdas	    (f_longform || f_size)) {
322284198Smarcel		xo_emit("{L:total} {:total-blocks/%lu}\n",
323284198Smarcel			howmany(dp->btotal, blocksize));
324130236Sdas	}
32596892Stjr
326284198Smarcel	xo_open_list("entry");
327102577Skeramida	base = 0;
3281556Srgrimes	for (row = 0; row < numrows; ++row) {
3291556Srgrimes		endcol = colwidth;
33096892Stjr		if (!f_sortacross)
33196892Stjr			base = row;
33296892Stjr		for (col = 0, chcnt = 0; col < numcols; ++col) {
333284198Smarcel			xo_open_instance("entry");
3341556Srgrimes			chcnt += printaname(array[base], dp->s_inode,
3351556Srgrimes			    dp->s_block);
336284198Smarcel			xo_close_instance("entry");
33796892Stjr			if (f_sortacross)
33896892Stjr				base++;
33996892Stjr			else
34096892Stjr				base += numrows;
34196892Stjr			if (base >= num)
3421556Srgrimes				break;
34337932Shoek			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
34488595Sjoe			    <= endcol) {
34596892Stjr				if (f_sortacross && col + 1 >= numcols)
34696892Stjr					break;
347284198Smarcel				xo_emit(f_notabs ? " " : "\t");
3481556Srgrimes				chcnt = cnt;
3491556Srgrimes			}
3501556Srgrimes			endcol += colwidth;
3511556Srgrimes		}
352284198Smarcel		xo_emit("\n");
3531556Srgrimes	}
354284198Smarcel	xo_close_list("entry");
3551556Srgrimes}
3561556Srgrimes
3571556Srgrimes/*
3581556Srgrimes * print [inode] [size] name
3591556Srgrimes * return # of characters printed, no trailing characters.
3601556Srgrimes */
3611556Srgrimesstatic int
362114583Smarkmprintaname(const FTSENT *p, u_long inodefield, u_long sizefield)
3631556Srgrimes{
3641556Srgrimes	struct stat *sp;
36588594Sjoe	int chcnt;
36661292Sache#ifdef COLORLS
36788594Sjoe	int color_printed = 0;
36861292Sache#endif
3691556Srgrimes
3701556Srgrimes	sp = p->fts_statp;
3711556Srgrimes	chcnt = 0;
3721556Srgrimes	if (f_inode)
373284198Smarcel		chcnt += xo_emit("{:inode/%*ju} ",
374241014Smdf		    (int)inodefield, (uintmax_t)sp->st_ino);
3751556Srgrimes	if (f_size)
376284198Smarcel		chcnt += xo_emit("{:size/%*jd} ",
3771556Srgrimes		    (int)sizefield, howmany(sp->st_blocks, blocksize));
37861268Sjoe#ifdef COLORLS
37961178Sjoe	if (f_color)
38061291Sache		color_printed = colortype(sp->st_mode);
38161268Sjoe#endif
382284198Smarcel	chcnt += printname("name", p->fts_name);
38361268Sjoe#ifdef COLORLS
38461291Sache	if (f_color && color_printed)
38561321Sache		endcolor(0);
38661268Sjoe#endif
3871556Srgrimes	if (f_type)
3881556Srgrimes		chcnt += printtype(sp->st_mode);
3891556Srgrimes	return (chcnt);
3901556Srgrimes}
3911556Srgrimes
392202945Sjh/*
393202945Sjh * Print device special file major and minor numbers.
394202945Sjh */
3951556Srgrimesstatic void
396202945Sjhprintdev(size_t width, dev_t dev)
397202945Sjh{
398284198Smarcel	xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev);
399202945Sjh}
400202945Sjh
401202945Sjhstatic void
402284198Smarcelprinttime(const char *field, time_t ftime)
4031556Srgrimes{
40488594Sjoe	char longstring[80];
405284198Smarcel	char fmt[BUFSIZ];
406114583Smarkm	static time_t now = 0;
40788594Sjoe	const char *format;
40888594Sjoe	static int d_first = -1;
4091556Srgrimes
41074566Sache	if (d_first < 0)
41174566Sache		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
41221545Smpp	if (now == 0)
41321545Smpp		now = time(NULL);
41421545Smpp
4159987Swollman#define	SIXMONTHS	((365 / 2) * 86400)
416177907Sgrog	if (f_timeformat)  /* user specified format */
417177907Sgrog		format = f_timeformat;
418177907Sgrog	else if (f_sectime)
41961920Sjoe		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
420177907Sgrog		format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
42121545Smpp	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
42261920Sjoe		/* mmm dd hh:mm || dd mmm hh:mm */
423177907Sgrog		format = d_first ? "%e %b %R" : "%b %e %R";
42461814Sjoe	else
42561920Sjoe		/* mmm dd  yyyy || dd mmm  yyyy */
426242840Speter		format = d_first ? "%e %b  %Y" : "%b %e  %Y";
42761814Sjoe	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
428284198Smarcel
429284198Smarcel	snprintf(fmt, sizeof(fmt), "{:%s/%%s} ", field);
430284198Smarcel	xo_attr("value", "%ld", (long) ftime);
431284198Smarcel	xo_emit(fmt, longstring);
4321556Srgrimes}
4331556Srgrimes
4341556Srgrimesstatic int
43590110Simpprinttype(u_int mode)
4361556Srgrimes{
43796892Stjr
43896892Stjr	if (f_slash) {
43996892Stjr		if ((mode & S_IFMT) == S_IFDIR) {
440284198Smarcel			xo_emit("{D:\\/}{e:type/directory}");
44196892Stjr			return (1);
44296892Stjr		}
44396892Stjr		return (0);
44496892Stjr	}
44596892Stjr
4461556Srgrimes	switch (mode & S_IFMT) {
4471556Srgrimes	case S_IFDIR:
448284198Smarcel		xo_emit("{D:/\\/}{e:type/directory}");
4491556Srgrimes		return (1);
4501556Srgrimes	case S_IFIFO:
451284198Smarcel		xo_emit("{D:|}{e:type/fifo}");
4521556Srgrimes		return (1);
4531556Srgrimes	case S_IFLNK:
454284198Smarcel		xo_emit("{D:@}{e:type/link}");
4551556Srgrimes		return (1);
4561556Srgrimes	case S_IFSOCK:
457284198Smarcel		xo_emit("{D:=}{e:type/socket}");
4581556Srgrimes		return (1);
45920417Ssteve	case S_IFWHT:
460284198Smarcel		xo_emit("{D:%}{e:type/whiteout}");
46120417Ssteve		return (1);
46290150Smarkm	default:
46396681Sbillf		break;
4641556Srgrimes	}
4651556Srgrimes	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
466284198Smarcel		xo_emit("{D:*}{e:executable/}");
4671556Srgrimes		return (1);
4681556Srgrimes	}
4691556Srgrimes	return (0);
4701556Srgrimes}
4711556Srgrimes
47261268Sjoe#ifdef COLORLS
47361323Sachestatic int
47490110Simpputch(int c)
47561291Sache{
476284198Smarcel	xo_emit("{D:/%c}", c);
47761321Sache	return 0;
47861291Sache}
47961291Sache
48061323Sachestatic int
48190110Simpwritech(int c)
48261321Sache{
483114583Smarkm	char tmp = (char)c;
48461291Sache
48588602Sjoe	(void)write(STDOUT_FILENO, &tmp, 1);
48661321Sache	return 0;
48761321Sache}
48861321Sache
48961323Sachestatic void
49090110Simpprintcolor(Colors c)
49161178Sjoe{
49288594Sjoe	char *ansiseq;
49361268Sjoe
49488583Sjoe	if (colors[c].bold)
49588583Sjoe		tputs(enter_bold, 1, putch);
49688583Sjoe
49788583Sjoe	if (colors[c].num[0] != -1) {
49888583Sjoe		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
49961321Sache		if (ansiseq)
50061291Sache			tputs(ansiseq, 1, putch);
50161178Sjoe	}
50288583Sjoe	if (colors[c].num[1] != -1) {
50388583Sjoe		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
50461321Sache		if (ansiseq)
50561291Sache			tputs(ansiseq, 1, putch);
50661268Sjoe	}
50761178Sjoe}
50861178Sjoe
50961321Sachestatic void
51090110Simpendcolor(int sig)
51161268Sjoe{
51261321Sache	tputs(ansi_coloff, 1, sig ? writech : putch);
51388583Sjoe	tputs(attrs_off, 1, sig ? writech : putch);
51461268Sjoe}
51561268Sjoe
51661321Sachestatic int
51790110Simpcolortype(mode_t mode)
51861178Sjoe{
51988602Sjoe	switch (mode & S_IFMT) {
52088595Sjoe	case S_IFDIR:
52161178Sjoe		if (mode & S_IWOTH)
52288595Sjoe			if (mode & S_ISTXT)
52388595Sjoe				printcolor(C_WSDIR);
52488595Sjoe			else
52588595Sjoe				printcolor(C_WDIR);
52661178Sjoe		else
52788595Sjoe			printcolor(C_DIR);
52888602Sjoe		return (1);
52988595Sjoe	case S_IFLNK:
53061178Sjoe		printcolor(C_LNK);
53188602Sjoe		return (1);
53288595Sjoe	case S_IFSOCK:
53361178Sjoe		printcolor(C_SOCK);
53488602Sjoe		return (1);
53588595Sjoe	case S_IFIFO:
53661178Sjoe		printcolor(C_FIFO);
53788602Sjoe		return (1);
53888595Sjoe	case S_IFBLK:
53961178Sjoe		printcolor(C_BLK);
54088602Sjoe		return (1);
54188595Sjoe	case S_IFCHR:
54261178Sjoe		printcolor(C_CHR);
54388602Sjoe		return (1);
544114583Smarkm	default:;
54561178Sjoe	}
54661178Sjoe	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
54761178Sjoe		if (mode & S_ISUID)
54888595Sjoe			printcolor(C_SUID);
54961178Sjoe		else if (mode & S_ISGID)
55088595Sjoe			printcolor(C_SGID);
55161178Sjoe		else
55288595Sjoe			printcolor(C_EXEC);
55388602Sjoe		return (1);
55461178Sjoe	}
55588602Sjoe	return (0);
55661178Sjoe}
55761178Sjoe
55861178Sjoevoid
55990110Simpparsecolors(const char *cs)
56061178Sjoe{
56188594Sjoe	int i;
56288594Sjoe	int j;
563105780Smarkm	size_t len;
56488594Sjoe	char c[2];
56588594Sjoe	short legacy_warn = 0;
56661321Sache
56788586Sjoe	if (cs == NULL)
56888602Sjoe		cs = "";	/* LSCOLORS not set */
56961178Sjoe	len = strlen(cs);
570114583Smarkm	for (i = 0; i < (int)C_NUMCOLORS; i++) {
57188583Sjoe		colors[i].bold = 0;
57288583Sjoe
573114583Smarkm		if (len <= 2 * (size_t)i) {
57488586Sjoe			c[0] = defcolors[2 * i];
57588586Sjoe			c[1] = defcolors[2 * i + 1];
57688602Sjoe		} else {
57788586Sjoe			c[0] = cs[2 * i];
57888586Sjoe			c[1] = cs[2 * i + 1];
57961178Sjoe		}
58088602Sjoe		for (j = 0; j < 2; j++) {
58188583Sjoe			/* Legacy colours used 0-7 */
58288583Sjoe			if (c[j] >= '0' && c[j] <= '7') {
58388583Sjoe				colors[i].num[j] = c[j] - '0';
58488583Sjoe				if (!legacy_warn) {
585284198Smarcel					xo_warnx("LSCOLORS should use "
58688588Sjoe					    "characters a-h instead of 0-9 ("
587106479Stjr					    "see the manual page)");
58888583Sjoe				}
58988583Sjoe				legacy_warn = 1;
59088583Sjoe			} else if (c[j] >= 'a' && c[j] <= 'h')
59188583Sjoe				colors[i].num[j] = c[j] - 'a';
59288583Sjoe			else if (c[j] >= 'A' && c[j] <= 'H') {
59388583Sjoe				colors[i].num[j] = c[j] - 'A';
59488583Sjoe				colors[i].bold = 1;
595114583Smarkm			} else if (tolower((unsigned char)c[j]) == 'x')
59688583Sjoe				colors[i].num[j] = -1;
59788583Sjoe			else {
598284198Smarcel				xo_warnx("invalid character '%c' in LSCOLORS"
599106479Stjr				    " env var", c[j]);
60088584Sjoe				colors[i].num[j] = -1;
60161178Sjoe			}
60261178Sjoe		}
60361178Sjoe	}
60461178Sjoe}
60561291Sache
60661323Sachevoid
60790110Simpcolorquit(int sig)
60861291Sache{
60961321Sache	endcolor(sig);
61061294Sache
61188602Sjoe	(void)signal(sig, SIG_DFL);
61288602Sjoe	(void)kill(getpid(), sig);
61361291Sache}
61488602Sjoe
61588602Sjoe#endif /* COLORLS */
61688602Sjoe
6171556Srgrimesstatic void
618105780Smarkmprintlink(const FTSENT *p)
6191556Srgrimes{
62088594Sjoe	int lnklen;
62188594Sjoe	char name[MAXPATHLEN + 1];
62288594Sjoe	char path[MAXPATHLEN + 1];
6231556Srgrimes
6241556Srgrimes	if (p->fts_level == FTS_ROOTLEVEL)
6251556Srgrimes		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
6268855Srgrimes	else
6271556Srgrimes		(void)snprintf(name, sizeof(name),
6281556Srgrimes		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
6291556Srgrimes	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
630284198Smarcel		xo_error("\nls: %s: %s\n", name, strerror(errno));
6311556Srgrimes		return;
6321556Srgrimes	}
6331556Srgrimes	path[lnklen] = '\0';
634284198Smarcel	xo_emit(" -> ");
635284198Smarcel	(void)printname("target", path);
6361556Srgrimes}
63788591Sjoe
63888591Sjoestatic void
639284198Smarcelprintsize(const char *field, size_t width, off_t bytes)
64088591Sjoe{
641284198Smarcel	char fmt[BUFSIZ];
642284198Smarcel
64388591Sjoe	if (f_humanval) {
644202945Sjh		/*
645202945Sjh		 * Reserve one space before the size and allocate room for
646202945Sjh		 * the trailing '\0'.
647202945Sjh		 */
648202945Sjh		char buf[HUMANVALSTR_LEN - 1 + 1];
64988591Sjoe
650129719Spjd		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
651129719Spjd		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
652284198Smarcel		snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width);
653284198Smarcel		xo_attr("value", "%jd", (intmax_t) bytes);
654284198Smarcel		xo_emit(fmt, buf);
655284198Smarcel	} else {		/* with commas */
656242725Sgrog		/* This format assignment needed to work round gcc bug. */
657284198Smarcel		snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ",
658284198Smarcel		     field, (int) width, f_thousands ? "'" : "");
659284198Smarcel		xo_emit(fmt, (intmax_t) bytes);
660284198Smarcel	}
66188591Sjoe}
66288591Sjoe
663196712Strasz/*
664196712Strasz * Add a + after the standard rwxrwxrwx mode if the file has an
665196712Strasz * ACL. strmode() reserves space at the end of the string.
666196712Strasz */
667106371Stjrstatic void
668196712Straszaclmode(char *buf, const FTSENT *p)
669106371Stjr{
670106371Stjr	char name[MAXPATHLEN + 1];
671196712Strasz	int ret, trivial;
672196712Strasz	static dev_t previous_dev = NODEV;
673196712Strasz	static int supports_acls = -1;
674196712Strasz	static int type = ACL_TYPE_ACCESS;
675106371Stjr	acl_t facl;
676106371Stjr
677106371Stjr	/*
678196712Strasz	 * XXX: ACLs are not supported on whiteouts and device files
679196712Strasz	 * residing on UFS.
680106371Stjr	 */
681196712Strasz	if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
682196712Strasz	    S_ISWHT(p->fts_statp->st_mode))
683108066Stjr		return;
684196712Strasz
685196773Strasz	if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
686196773Strasz		return;
687196773Strasz
688196773Strasz	if (p->fts_level == FTS_ROOTLEVEL)
689196773Strasz		snprintf(name, sizeof(name), "%s", p->fts_name);
690196773Strasz	else
691196773Strasz		snprintf(name, sizeof(name), "%s/%s",
692196773Strasz		    p->fts_parent->fts_accpath, p->fts_name);
693196773Strasz
694196712Strasz	if (previous_dev != p->fts_statp->st_dev) {
695196712Strasz		previous_dev = p->fts_statp->st_dev;
696196712Strasz		supports_acls = 0;
697196712Strasz
698196712Strasz		ret = lpathconf(name, _PC_ACL_NFS4);
699196712Strasz		if (ret > 0) {
700196712Strasz			type = ACL_TYPE_NFS4;
701196712Strasz			supports_acls = 1;
702196712Strasz		} else if (ret < 0 && errno != EINVAL) {
703284198Smarcel			xo_warn("%s", name);
704196712Strasz			return;
705196712Strasz		}
706196712Strasz		if (supports_acls == 0) {
707196712Strasz			ret = lpathconf(name, _PC_ACL_EXTENDED);
708196712Strasz			if (ret > 0) {
709196712Strasz				type = ACL_TYPE_ACCESS;
710196712Strasz				supports_acls = 1;
711196712Strasz			} else if (ret < 0 && errno != EINVAL) {
712284198Smarcel				xo_warn("%s", name);
713196712Strasz				return;
714196712Strasz			}
715196712Strasz		}
716108066Stjr	}
717196712Strasz	if (supports_acls == 0)
718106371Stjr		return;
719196712Strasz	facl = acl_get_link_np(name, type);
720196712Strasz	if (facl == NULL) {
721284198Smarcel		xo_warn("%s", name);
722196712Strasz		return;
723106371Stjr	}
724196712Strasz	if (acl_is_trivial_np(facl, &trivial)) {
725106371Stjr		acl_free(facl);
726284198Smarcel		xo_warn("%s", name);
727196712Strasz		return;
728196712Strasz	}
729196712Strasz	if (!trivial)
730196712Strasz		buf[10] = '+';
731196712Strasz	acl_free(facl);
732106371Stjr}
733