print.c revision 177907
1254721Semaste/*-
2254721Semaste * Copyright (c) 1989, 1993, 1994
3254721Semaste *	The Regents of the University of California.  All rights reserved.
4254721Semaste *
5254721Semaste * This code is derived from software contributed to Berkeley by
6254721Semaste * Michael Fischbein.
7254721Semaste *
8254721Semaste * Redistribution and use in source and binary forms, with or without
9254721Semaste * modification, are permitted provided that the following conditions
10254721Semaste * are met:
11254721Semaste * 1. Redistributions of source code must retain the above copyright
12254721Semaste *    notice, this list of conditions and the following disclaimer.
13254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
14254721Semaste *    notice, this list of conditions and the following disclaimer in the
15254721Semaste *    documentation and/or other materials provided with the distribution.
16254721Semaste * 4. Neither the name of the University nor the names of its contributors
17254721Semaste *    may be used to endorse or promote products derived from this software
18254721Semaste *    without specific prior written permission.
19254721Semaste *
20254721Semaste * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23254721Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24254721Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25254721Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30254721Semaste * SUCH DAMAGE.
31254721Semaste */
32254721Semaste
33254721Semaste#if 0
34254721Semaste#ifndef lint
35254721Semastestatic char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
36254721Semaste#endif /* not lint */
37254721Semaste#endif
38254721Semaste#include <sys/cdefs.h>
39254721Semaste__FBSDID("$FreeBSD: head/bin/ls/print.c 177907 2008-04-04 03:57:46Z grog $");
40254721Semaste
41254721Semaste#include <sys/param.h>
42254721Semaste#include <sys/stat.h>
43254721Semaste#include <sys/acl.h>
44254721Semaste
45254721Semaste#include <err.h>
46254721Semaste#include <errno.h>
47254721Semaste#include <fts.h>
48254721Semaste#include <langinfo.h>
49254721Semaste#include <libutil.h>
50254721Semaste#include <stdio.h>
51254721Semaste#include <stdlib.h>
52254721Semaste#include <string.h>
53254721Semaste#include <time.h>
54254721Semaste#include <unistd.h>
55254721Semaste#ifdef COLORLS
56254721Semaste#include <ctype.h>
57254721Semaste#include <termcap.h>
58254721Semaste#include <signal.h>
59254721Semaste#endif
60254721Semaste
61254721Semaste#include "ls.h"
62254721Semaste#include "extern.h"
63254721Semaste
64254721Semastestatic int	printaname(const FTSENT *, u_long, u_long);
65254721Semastestatic void	printlink(const FTSENT *);
66254721Semastestatic void	printtime(time_t);
67254721Semastestatic int	printtype(u_int);
68254721Semastestatic void	printsize(size_t, off_t);
69254721Semaste#ifdef COLORLS
70254721Semastestatic void	endcolor(int);
71254721Semastestatic int	colortype(mode_t);
72254721Semaste#endif
73254721Semastestatic void	aclmode(char *, const FTSENT *, int *);
74254721Semaste
75254721Semaste#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
76254721Semaste
77254721Semaste#ifdef COLORLS
78254721Semaste/* Most of these are taken from <sys/stat.h> */
79254721Semastetypedef enum Colors {
80254721Semaste	C_DIR,			/* directory */
81254721Semaste	C_LNK,			/* symbolic link */
82254721Semaste	C_SOCK,			/* socket */
83254721Semaste	C_FIFO,			/* pipe */
84254721Semaste	C_EXEC,			/* executable */
85254721Semaste	C_BLK,			/* block special */
86254721Semaste	C_CHR,			/* character special */
87254721Semaste	C_SUID,			/* setuid executable */
88254721Semaste	C_SGID,			/* setgid executable */
89254721Semaste	C_WSDIR,		/* directory writeble to others, with sticky
90254721Semaste				 * bit */
91254721Semaste	C_WDIR,			/* directory writeble to others, without
92254721Semaste				 * sticky bit */
93254721Semaste	C_NUMCOLORS		/* just a place-holder */
94254721Semaste} Colors;
95254721Semaste
96254721Semastestatic const char *defcolors = "exfxcxdxbxegedabagacad";
97254721Semaste
98254721Semaste/* colors for file types */
99254721Semastestatic struct {
100254721Semaste	int	num[2];
101254721Semaste	int	bold;
102254721Semaste} colors[C_NUMCOLORS];
103254721Semaste#endif
104254721Semaste
105254721Semastevoid
106254721Semasteprintscol(const DISPLAY *dp)
107254721Semaste{
108254721Semaste	FTSENT *p;
109254721Semaste
110254721Semaste	for (p = dp->list; p; p = p->fts_link) {
111254721Semaste		if (IS_NOPRINT(p))
112254721Semaste			continue;
113254721Semaste		(void)printaname(p, dp->s_inode, dp->s_block);
114254721Semaste		(void)putchar('\n');
115254721Semaste	}
116254721Semaste}
117254721Semaste
118254721Semaste/*
119254721Semaste * print name in current style
120254721Semaste */
121254721Semasteint
122254721Semasteprintname(const char *name)
123254721Semaste{
124254721Semaste	if (f_octal || f_octal_escape)
125254721Semaste		return prn_octal(name);
126254721Semaste	else if (f_nonprint)
127254721Semaste		return prn_printable(name);
128254721Semaste	else
129254721Semaste		return prn_normal(name);
130254721Semaste}
131254721Semaste
132254721Semastevoid
133254721Semasteprintlong(const DISPLAY *dp)
134254721Semaste{
135254721Semaste	struct stat *sp;
136254721Semaste	FTSENT *p;
137254721Semaste	NAMES *np;
138254721Semaste	char buf[20];
139254721Semaste#ifdef COLORLS
140254721Semaste	int color_printed = 0;
141254721Semaste#endif
142254721Semaste	int haveacls;
143254721Semaste	dev_t prevdev;
144254721Semaste
145254721Semaste	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
146254721Semaste	    (f_longform || f_size)) {
147254721Semaste		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
148254721Semaste	}
149254721Semaste
150254721Semaste	haveacls = 1;
151254721Semaste	prevdev = (dev_t)-1;
152254721Semaste	for (p = dp->list; p; p = p->fts_link) {
153254721Semaste		if (IS_NOPRINT(p))
154254721Semaste			continue;
155254721Semaste		sp = p->fts_statp;
156254721Semaste		if (f_inode)
157254721Semaste			(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
158254721Semaste		if (f_size)
159254721Semaste			(void)printf("%*jd ",
160254721Semaste			    dp->s_block, howmany(sp->st_blocks, blocksize));
161254721Semaste		strmode(sp->st_mode, buf);
162254721Semaste		/*
163254721Semaste		 * Cache whether or not the filesystem supports ACL's to
164254721Semaste		 * avoid expensive syscalls. Try again when we change devices.
165254721Semaste		 */
166254721Semaste		if (haveacls || sp->st_dev != prevdev) {
167254721Semaste			aclmode(buf, p, &haveacls);
168254721Semaste			prevdev = sp->st_dev;
169254721Semaste		}
170254721Semaste		np = p->fts_pointer;
171254721Semaste		(void)printf("%s %*u %-*s  %-*s	 ", buf, dp->s_nlink,
172254721Semaste		    sp->st_nlink, dp->s_user, np->user, dp->s_group,
173254721Semaste		    np->group);
174254721Semaste		if (f_flags)
175254721Semaste			(void)printf("%-*s ", dp->s_flags, np->flags);
176254721Semaste		if (f_label)
177254721Semaste			(void)printf("%-*s ", dp->s_label, np->label);
178254721Semaste		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
179254721Semaste			if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
180254721Semaste				(void)printf("%3d, 0x%08x ",
181254721Semaste				    major(sp->st_rdev),
182254721Semaste				    (u_int)minor(sp->st_rdev));
183254721Semaste			else
184254721Semaste				(void)printf("%3d, %3d ",
185254721Semaste				    major(sp->st_rdev), minor(sp->st_rdev));
186254721Semaste		else if (dp->bcfile)
187254721Semaste			(void)printf("%*s%*jd ",
188254721Semaste			    8 - dp->s_size, "", dp->s_size, sp->st_size);
189254721Semaste		else
190254721Semaste			printsize(dp->s_size, sp->st_size);
191254721Semaste		if (f_accesstime)
192254721Semaste			printtime(sp->st_atime);
193254721Semaste		else if (f_birthtime)
194254721Semaste			printtime(sp->st_birthtime);
195254721Semaste		else if (f_statustime)
196254721Semaste			printtime(sp->st_ctime);
197254721Semaste		else
198254721Semaste			printtime(sp->st_mtime);
199254721Semaste#ifdef COLORLS
200254721Semaste		if (f_color)
201254721Semaste			color_printed = colortype(sp->st_mode);
202254721Semaste#endif
203254721Semaste		(void)printname(p->fts_name);
204254721Semaste#ifdef COLORLS
205254721Semaste		if (f_color && color_printed)
206254721Semaste			endcolor(0);
207254721Semaste#endif
208254721Semaste		if (f_type)
209254721Semaste			(void)printtype(sp->st_mode);
210254721Semaste		if (S_ISLNK(sp->st_mode))
211254721Semaste			printlink(p);
212254721Semaste		(void)putchar('\n');
213254721Semaste	}
214254721Semaste}
215254721Semaste
216254721Semastevoid
217254721Semasteprintstream(const DISPLAY *dp)
218254721Semaste{
219254721Semaste	FTSENT *p;
220254721Semaste	int chcnt;
221254721Semaste
222254721Semaste	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
223254721Semaste		if (p->fts_number == NO_PRINT)
224254721Semaste			continue;
225254721Semaste		/* XXX strlen does not take octal escapes into account. */
226254721Semaste		if (strlen(p->fts_name) + chcnt +
227254721Semaste		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
228254721Semaste			putchar('\n');
229254721Semaste			chcnt = 0;
230254721Semaste		}
231254721Semaste		chcnt += printaname(p, dp->s_inode, dp->s_block);
232254721Semaste		if (p->fts_link) {
233254721Semaste			printf(", ");
234254721Semaste			chcnt += 2;
235254721Semaste		}
236254721Semaste	}
237254721Semaste	if (chcnt)
238254721Semaste		putchar('\n');
239254721Semaste}
240254721Semaste
241254721Semastevoid
242254721Semasteprintcol(const DISPLAY *dp)
243254721Semaste{
244254721Semaste	static FTSENT **array;
245254721Semaste	static int lastentries = -1;
246254721Semaste	FTSENT *p;
247254721Semaste	FTSENT **narray;
248254721Semaste	int base;
249254721Semaste	int chcnt;
250254721Semaste	int cnt;
251254721Semaste	int col;
252254721Semaste	int colwidth;
253254721Semaste	int endcol;
254254721Semaste	int num;
255254721Semaste	int numcols;
256254721Semaste	int numrows;
257254721Semaste	int row;
258254721Semaste	int tabwidth;
259254721Semaste
260254721Semaste	if (f_notabs)
261254721Semaste		tabwidth = 1;
262254721Semaste	else
263254721Semaste		tabwidth = 8;
264254721Semaste
265254721Semaste	/*
266254721Semaste	 * Have to do random access in the linked list -- build a table
267254721Semaste	 * of pointers.
268254721Semaste	 */
269254721Semaste	if (dp->entries > lastentries) {
270254721Semaste		if ((narray =
271254721Semaste		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
272254721Semaste			warn(NULL);
273254721Semaste			printscol(dp);
274254721Semaste			return;
275254721Semaste		}
276254721Semaste		lastentries = dp->entries;
277254721Semaste		array = narray;
278254721Semaste	}
279254721Semaste	for (p = dp->list, num = 0; p; p = p->fts_link)
280254721Semaste		if (p->fts_number != NO_PRINT)
281254721Semaste			array[num++] = p;
282254721Semaste
283254721Semaste	colwidth = dp->maxlen;
284254721Semaste	if (f_inode)
285254721Semaste		colwidth += dp->s_inode + 1;
286254721Semaste	if (f_size)
287254721Semaste		colwidth += dp->s_block + 1;
288254721Semaste	if (f_type)
289254721Semaste		colwidth += 1;
290254721Semaste
291254721Semaste	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
292254721Semaste	if (termwidth < 2 * colwidth) {
293254721Semaste		printscol(dp);
294254721Semaste		return;
295254721Semaste	}
296254721Semaste	numcols = termwidth / colwidth;
297254721Semaste	numrows = num / numcols;
298254721Semaste	if (num % numcols)
299254721Semaste		++numrows;
300254721Semaste
301254721Semaste	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
302254721Semaste	    (f_longform || f_size)) {
303254721Semaste		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
304254721Semaste	}
305254721Semaste
306254721Semaste	base = 0;
307254721Semaste	for (row = 0; row < numrows; ++row) {
308254721Semaste		endcol = colwidth;
309254721Semaste		if (!f_sortacross)
310254721Semaste			base = row;
311254721Semaste		for (col = 0, chcnt = 0; col < numcols; ++col) {
312254721Semaste			chcnt += printaname(array[base], dp->s_inode,
313254721Semaste			    dp->s_block);
314254721Semaste			if (f_sortacross)
315254721Semaste				base++;
316254721Semaste			else
317254721Semaste				base += numrows;
318254721Semaste			if (base >= num)
319254721Semaste				break;
320254721Semaste			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
321254721Semaste			    <= endcol) {
322254721Semaste				if (f_sortacross && col + 1 >= numcols)
323254721Semaste					break;
324254721Semaste				(void)putchar(f_notabs ? ' ' : '\t');
325254721Semaste				chcnt = cnt;
326254721Semaste			}
327254721Semaste			endcol += colwidth;
328254721Semaste		}
329254721Semaste		(void)putchar('\n');
330254721Semaste	}
331254721Semaste}
332254721Semaste
333254721Semaste/*
334254721Semaste * print [inode] [size] name
335254721Semaste * return # of characters printed, no trailing characters.
336254721Semaste */
337254721Semastestatic int
338254721Semasteprintaname(const FTSENT *p, u_long inodefield, u_long sizefield)
339254721Semaste{
340254721Semaste	struct stat *sp;
341254721Semaste	int chcnt;
342254721Semaste#ifdef COLORLS
343254721Semaste	int color_printed = 0;
344254721Semaste#endif
345254721Semaste
346254721Semaste	sp = p->fts_statp;
347254721Semaste	chcnt = 0;
348254721Semaste	if (f_inode)
349254721Semaste		chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
350254721Semaste	if (f_size)
351254721Semaste		chcnt += printf("%*jd ",
352254721Semaste		    (int)sizefield, howmany(sp->st_blocks, blocksize));
353254721Semaste#ifdef COLORLS
354254721Semaste	if (f_color)
355254721Semaste		color_printed = colortype(sp->st_mode);
356254721Semaste#endif
357254721Semaste	chcnt += printname(p->fts_name);
358254721Semaste#ifdef COLORLS
359254721Semaste	if (f_color && color_printed)
360254721Semaste		endcolor(0);
361254721Semaste#endif
362254721Semaste	if (f_type)
363254721Semaste		chcnt += printtype(sp->st_mode);
364254721Semaste	return (chcnt);
365254721Semaste}
366254721Semaste
367254721Semastestatic void
368254721Semasteprinttime(time_t ftime)
369254721Semaste{
370254721Semaste	char longstring[80];
371254721Semaste	static time_t now = 0;
372254721Semaste	const char *format;
373254721Semaste	static int d_first = -1;
374254721Semaste
375254721Semaste	if (d_first < 0)
376254721Semaste		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
377254721Semaste	if (now == 0)
378254721Semaste		now = time(NULL);
379254721Semaste
380254721Semaste#define	SIXMONTHS	((365 / 2) * 86400)
381254721Semaste	if (f_timeformat)  /* user specified format */
382254721Semaste		format = f_timeformat;
383254721Semaste	else if (f_sectime)
384254721Semaste		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
385254721Semaste		format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
386254721Semaste	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
387254721Semaste		/* mmm dd hh:mm || dd mmm hh:mm */
388254721Semaste		format = d_first ? "%e %b %R" : "%b %e %R";
389254721Semaste	else
390254721Semaste		/* mmm dd  yyyy || dd mmm  yyyy */
391254721Semaste		format = d_first ? "%e %b  %Y" : "%b %e	 %Y";
392254721Semaste	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
393254721Semaste	fputs(longstring, stdout);
394254721Semaste	fputc(' ', stdout);
395254721Semaste}
396254721Semaste
397254721Semastestatic int
398254721Semasteprinttype(u_int mode)
399254721Semaste{
400254721Semaste
401254721Semaste	if (f_slash) {
402254721Semaste		if ((mode & S_IFMT) == S_IFDIR) {
403254721Semaste			(void)putchar('/');
404254721Semaste			return (1);
405254721Semaste		}
406254721Semaste		return (0);
407254721Semaste	}
408254721Semaste
409254721Semaste	switch (mode & S_IFMT) {
410254721Semaste	case S_IFDIR:
411254721Semaste		(void)putchar('/');
412254721Semaste		return (1);
413254721Semaste	case S_IFIFO:
414254721Semaste		(void)putchar('|');
415254721Semaste		return (1);
416254721Semaste	case S_IFLNK:
417254721Semaste		(void)putchar('@');
418254721Semaste		return (1);
419254721Semaste	case S_IFSOCK:
420254721Semaste		(void)putchar('=');
421254721Semaste		return (1);
422254721Semaste	case S_IFWHT:
423254721Semaste		(void)putchar('%');
424254721Semaste		return (1);
425254721Semaste	default:
426254721Semaste		break;
427254721Semaste	}
428254721Semaste	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
429254721Semaste		(void)putchar('*');
430254721Semaste		return (1);
431254721Semaste	}
432254721Semaste	return (0);
433254721Semaste}
434254721Semaste
435254721Semaste#ifdef COLORLS
436254721Semastestatic int
437254721Semasteputch(int c)
438254721Semaste{
439254721Semaste	(void)putchar(c);
440254721Semaste	return 0;
441254721Semaste}
442254721Semaste
443254721Semastestatic int
444254721Semastewritech(int c)
445254721Semaste{
446254721Semaste	char tmp = (char)c;
447254721Semaste
448254721Semaste	(void)write(STDOUT_FILENO, &tmp, 1);
449254721Semaste	return 0;
450254721Semaste}
451254721Semaste
452254721Semastestatic void
453254721Semasteprintcolor(Colors c)
454254721Semaste{
455254721Semaste	char *ansiseq;
456254721Semaste
457254721Semaste	if (colors[c].bold)
458254721Semaste		tputs(enter_bold, 1, putch);
459254721Semaste
460254721Semaste	if (colors[c].num[0] != -1) {
461254721Semaste		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
462254721Semaste		if (ansiseq)
463254721Semaste			tputs(ansiseq, 1, putch);
464254721Semaste	}
465254721Semaste	if (colors[c].num[1] != -1) {
466254721Semaste		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
467254721Semaste		if (ansiseq)
468254721Semaste			tputs(ansiseq, 1, putch);
469254721Semaste	}
470254721Semaste}
471254721Semaste
472254721Semastestatic void
473254721Semasteendcolor(int sig)
474254721Semaste{
475254721Semaste	tputs(ansi_coloff, 1, sig ? writech : putch);
476254721Semaste	tputs(attrs_off, 1, sig ? writech : putch);
477254721Semaste}
478254721Semaste
479254721Semastestatic int
480254721Semastecolortype(mode_t mode)
481254721Semaste{
482254721Semaste	switch (mode & S_IFMT) {
483254721Semaste	case S_IFDIR:
484254721Semaste		if (mode & S_IWOTH)
485254721Semaste			if (mode & S_ISTXT)
486254721Semaste				printcolor(C_WSDIR);
487254721Semaste			else
488254721Semaste				printcolor(C_WDIR);
489254721Semaste		else
490254721Semaste			printcolor(C_DIR);
491254721Semaste		return (1);
492254721Semaste	case S_IFLNK:
493254721Semaste		printcolor(C_LNK);
494254721Semaste		return (1);
495254721Semaste	case S_IFSOCK:
496254721Semaste		printcolor(C_SOCK);
497254721Semaste		return (1);
498254721Semaste	case S_IFIFO:
499254721Semaste		printcolor(C_FIFO);
500254721Semaste		return (1);
501254721Semaste	case S_IFBLK:
502254721Semaste		printcolor(C_BLK);
503254721Semaste		return (1);
504254721Semaste	case S_IFCHR:
505254721Semaste		printcolor(C_CHR);
506254721Semaste		return (1);
507254721Semaste	default:;
508254721Semaste	}
509254721Semaste	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
510254721Semaste		if (mode & S_ISUID)
511254721Semaste			printcolor(C_SUID);
512254721Semaste		else if (mode & S_ISGID)
513254721Semaste			printcolor(C_SGID);
514254721Semaste		else
515254721Semaste			printcolor(C_EXEC);
516254721Semaste		return (1);
517254721Semaste	}
518254721Semaste	return (0);
519254721Semaste}
520254721Semaste
521254721Semastevoid
522254721Semasteparsecolors(const char *cs)
523254721Semaste{
524254721Semaste	int i;
525254721Semaste	int j;
526254721Semaste	size_t len;
527254721Semaste	char c[2];
528254721Semaste	short legacy_warn = 0;
529254721Semaste
530254721Semaste	if (cs == NULL)
531254721Semaste		cs = "";	/* LSCOLORS not set */
532254721Semaste	len = strlen(cs);
533254721Semaste	for (i = 0; i < (int)C_NUMCOLORS; i++) {
534254721Semaste		colors[i].bold = 0;
535254721Semaste
536254721Semaste		if (len <= 2 * (size_t)i) {
537254721Semaste			c[0] = defcolors[2 * i];
538254721Semaste			c[1] = defcolors[2 * i + 1];
539254721Semaste		} else {
540254721Semaste			c[0] = cs[2 * i];
541254721Semaste			c[1] = cs[2 * i + 1];
542254721Semaste		}
543254721Semaste		for (j = 0; j < 2; j++) {
544254721Semaste			/* Legacy colours used 0-7 */
545254721Semaste			if (c[j] >= '0' && c[j] <= '7') {
546254721Semaste				colors[i].num[j] = c[j] - '0';
547254721Semaste				if (!legacy_warn) {
548254721Semaste					warnx("LSCOLORS should use "
549254721Semaste					    "characters a-h instead of 0-9 ("
550254721Semaste					    "see the manual page)");
551254721Semaste				}
552254721Semaste				legacy_warn = 1;
553254721Semaste			} else if (c[j] >= 'a' && c[j] <= 'h')
554254721Semaste				colors[i].num[j] = c[j] - 'a';
555254721Semaste			else if (c[j] >= 'A' && c[j] <= 'H') {
556254721Semaste				colors[i].num[j] = c[j] - 'A';
557254721Semaste				colors[i].bold = 1;
558254721Semaste			} else if (tolower((unsigned char)c[j]) == 'x')
559254721Semaste				colors[i].num[j] = -1;
560254721Semaste			else {
561254721Semaste				warnx("invalid character '%c' in LSCOLORS"
562254721Semaste				    " env var", c[j]);
563254721Semaste				colors[i].num[j] = -1;
564254721Semaste			}
565254721Semaste		}
566254721Semaste	}
567254721Semaste}
568254721Semaste
569254721Semastevoid
570254721Semastecolorquit(int sig)
571254721Semaste{
572254721Semaste	endcolor(sig);
573254721Semaste
574254721Semaste	(void)signal(sig, SIG_DFL);
575254721Semaste	(void)kill(getpid(), sig);
576254721Semaste}
577254721Semaste
578254721Semaste#endif /* COLORLS */
579254721Semaste
580254721Semastestatic void
581254721Semasteprintlink(const FTSENT *p)
582254721Semaste{
583254721Semaste	int lnklen;
584254721Semaste	char name[MAXPATHLEN + 1];
585254721Semaste	char path[MAXPATHLEN + 1];
586254721Semaste
587254721Semaste	if (p->fts_level == FTS_ROOTLEVEL)
588254721Semaste		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
589254721Semaste	else
590254721Semaste		(void)snprintf(name, sizeof(name),
591254721Semaste		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
592254721Semaste	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
593254721Semaste		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
594254721Semaste		return;
595254721Semaste	}
596254721Semaste	path[lnklen] = '\0';
597254721Semaste	(void)printf(" -> ");
598254721Semaste	(void)printname(path);
599254721Semaste}
600254721Semaste
601254721Semastestatic void
602254721Semasteprintsize(size_t width, off_t bytes)
603254721Semaste{
604254721Semaste
605254721Semaste	if (f_humanval) {
606254721Semaste		char buf[5];
607254721Semaste
608254721Semaste		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
609254721Semaste		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
610254721Semaste		(void)printf("%5s ", buf);
611254721Semaste	} else
612254721Semaste		(void)printf("%*jd ", (u_int)width, bytes);
613254721Semaste}
614254721Semaste
615254721Semastestatic void
616254721Semasteaclmode(char *buf, const FTSENT *p, int *haveacls)
617254721Semaste{
618254721Semaste	char name[MAXPATHLEN + 1];
619254721Semaste	int entries, ret;
620254721Semaste	acl_t facl;
621254721Semaste	acl_entry_t ae;
622254721Semaste
623254721Semaste	/*
624254721Semaste	 * Add a + after the standard rwxrwxrwx mode if the file has an
625254721Semaste	 * extended ACL. strmode() reserves space at the end of the string.
626254721Semaste	 */
627254721Semaste	if (p->fts_level == FTS_ROOTLEVEL)
628254721Semaste		snprintf(name, sizeof(name), "%s", p->fts_name);
629254721Semaste	else
630254721Semaste		snprintf(name, sizeof(name), "%s/%s",
631254721Semaste		    p->fts_parent->fts_accpath, p->fts_name);
632254721Semaste	/*
633254721Semaste	 * We have no way to tell whether a symbolic link has an ACL since
634254721Semaste	 * pathconf() and acl_get_file() both follow them.  They also don't
635254721Semaste	 * support whiteouts.
636254721Semaste	 */
637254721Semaste	if (S_ISLNK(p->fts_statp->st_mode) || S_ISWHT(p->fts_statp->st_mode)) {
638254721Semaste		*haveacls = 1;
639254721Semaste		return;
640254721Semaste	}
641254721Semaste	if ((ret = pathconf(name, _PC_ACL_EXTENDED)) <= 0) {
642254721Semaste		if (ret < 0 && errno != EINVAL)
643254721Semaste			warn("%s", name);
644254721Semaste		else
645254721Semaste			*haveacls = 0;
646254721Semaste		return;
647254721Semaste	}
648254721Semaste	*haveacls = 1;
649254721Semaste	if ((facl = acl_get_file(name, ACL_TYPE_ACCESS)) != NULL) {
650254721Semaste		if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 1) {
651254721Semaste			entries = 1;
652254721Semaste			while (acl_get_entry(facl, ACL_NEXT_ENTRY, &ae) == 1)
653254721Semaste				if (++entries > 3)
654263363Semaste					break;
655254721Semaste			/*
656254721Semaste			 * POSIX.1e requires that ACLs of type ACL_TYPE_ACCESS
657254721Semaste			 * must have at least three entries (owner, group,
658254721Semaste			 * and other). So anything with more than 3 ACLs looks
659254721Semaste			 * interesting to us.
660254721Semaste			 */
661254721Semaste			if (entries > 3)
662254721Semaste				buf[10] = '+';
663254721Semaste		}
664254721Semaste		acl_free(facl);
665254721Semaste	} else
666254721Semaste		warn("%s", name);
667254721Semaste}
668254721Semaste