print.c revision 130236
1321369Sdim/*
2303231Sdim * Copyright (c) 1989, 1993, 1994
3353358Sdim *	The Regents of the University of California.  All rights reserved.
4353358Sdim *
5353358Sdim * This code is derived from software contributed to Berkeley by
6303231Sdim * Michael Fischbein.
7303231Sdim *
8303231Sdim * Redistribution and use in source and binary forms, with or without
9303231Sdim * modification, are permitted provided that the following conditions
10303231Sdim * are met:
11303231Sdim * 1. Redistributions of source code must retain the above copyright
12303231Sdim *    notice, this list of conditions and the following disclaimer.
13303231Sdim * 2. Redistributions in binary form must reproduce the above copyright
14303231Sdim *    notice, this list of conditions and the following disclaimer in the
15303231Sdim *    documentation and/or other materials provided with the distribution.
16344779Sdim * 4. Neither the name of the University nor the names of its contributors
17344779Sdim *    may be used to endorse or promote products derived from this software
18314564Sdim *    without specific prior written permission.
19303231Sdim *
20303231Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21314564Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22314564Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23314564Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24303231Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25321369Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26303231Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27341825Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28303231Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29314564Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30314564Sdim * SUCH DAMAGE.
31314564Sdim */
32314564Sdim
33314564Sdim#if 0
34314564Sdim#ifndef lint
35314564Sdimstatic char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
36314564Sdim#endif /* not lint */
37314564Sdim#endif
38314564Sdim#include <sys/cdefs.h>
39314564Sdim__FBSDID("$FreeBSD: head/bin/ls/print.c 130236 2004-06-08 09:27:42Z das $");
40303231Sdim
41303231Sdim#include <sys/param.h>
42303231Sdim#include <sys/stat.h>
43303231Sdim#include <sys/acl.h>
44314564Sdim
45303231Sdim#include <err.h>
46303231Sdim#include <errno.h>
47303231Sdim#include <fts.h>
48303231Sdim#include <langinfo.h>
49303231Sdim#include <libutil.h>
50314564Sdim#include <stdio.h>
51303231Sdim#include <stdlib.h>
52303231Sdim#include <string.h>
53303231Sdim#include <time.h>
54303231Sdim#include <unistd.h>
55303231Sdim#ifdef COLORLS
56303231Sdim#include <ctype.h>
57303231Sdim#include <termcap.h>
58303231Sdim#include <signal.h>
59303231Sdim#endif
60303231Sdim
61303231Sdim#include "ls.h"
62303231Sdim#include "extern.h"
63303231Sdim
64303231Sdimstatic int	printaname(const FTSENT *, u_long, u_long);
65303231Sdimstatic void	printlink(const FTSENT *);
66303231Sdimstatic void	printtime(time_t);
67303231Sdimstatic int	printtype(u_int);
68303231Sdimstatic void	printsize(size_t, off_t);
69321369Sdim#ifdef COLORLS
70321369Sdimstatic void	endcolor(int);
71321369Sdimstatic int	colortype(mode_t);
72321369Sdim#endif
73321369Sdimstatic void	aclmode(char *, const FTSENT *, int *);
74321369Sdim
75303231Sdim#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
76303231Sdim
77303231Sdim#ifdef COLORLS
78303231Sdim/* Most of these are taken from <sys/stat.h> */
79303231Sdimtypedef enum Colors {
80303231Sdim	C_DIR,			/* directory */
81303231Sdim	C_LNK,			/* symbolic link */
82303231Sdim	C_SOCK,			/* socket */
83303231Sdim	C_FIFO,			/* pipe */
84303231Sdim	C_EXEC,			/* executable */
85303231Sdim	C_BLK,			/* block special */
86303231Sdim	C_CHR,			/* character special */
87303231Sdim	C_SUID,			/* setuid executable */
88314564Sdim	C_SGID,			/* setgid executable */
89303231Sdim	C_WSDIR,		/* directory writeble to others, with sticky
90303231Sdim				 * bit */
91303231Sdim	C_WDIR,			/* directory writeble to others, without
92303231Sdim				 * sticky bit */
93303231Sdim	C_NUMCOLORS		/* just a place-holder */
94303231Sdim} Colors;
95303231Sdim
96303231Sdimstatic const char *defcolors = "exfxcxdxbxegedabagacad";
97303231Sdim
98303231Sdim/* colors for file types */
99303231Sdimstatic struct {
100303231Sdim	int	num[2];
101303231Sdim	int	bold;
102303231Sdim} colors[C_NUMCOLORS];
103303231Sdim#endif
104303231Sdim
105303231Sdimvoid
106303231Sdimprintscol(const DISPLAY *dp)
107303231Sdim{
108314564Sdim	FTSENT *p;
109303231Sdim
110303231Sdim	for (p = dp->list; p; p = p->fts_link) {
111303231Sdim		if (IS_NOPRINT(p))
112303231Sdim			continue;
113303231Sdim		(void)printaname(p, dp->s_inode, dp->s_block);
114303231Sdim		(void)putchar('\n');
115314564Sdim	}
116303231Sdim}
117303231Sdim
118303231Sdim/*
119303231Sdim * print name in current style
120303231Sdim */
121303231Sdimint
122303231Sdimprintname(const char *name)
123303231Sdim{
124314564Sdim	if (f_octal || f_octal_escape)
125303231Sdim		return prn_octal(name);
126303231Sdim	else if (f_nonprint)
127303231Sdim		return prn_printable(name);
128303231Sdim	else
129303231Sdim		return prn_normal(name);
130303231Sdim}
131303231Sdim
132303231Sdimvoid
133303231Sdimprintlong(const DISPLAY *dp)
134303231Sdim{
135303231Sdim	struct stat *sp;
136303231Sdim	FTSENT *p;
137303231Sdim	NAMES *np;
138303231Sdim	char buf[20];
139303231Sdim#ifdef COLORLS
140303231Sdim	int color_printed = 0;
141303231Sdim#endif
142303231Sdim	int haveacls;
143303231Sdim	dev_t prevdev;
144303231Sdim
145303231Sdim	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
146303231Sdim	    (f_longform || f_size)) {
147314564Sdim		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
148303231Sdim	}
149303231Sdim
150303231Sdim	haveacls = 1;
151303231Sdim	prevdev = (dev_t)-1;
152303231Sdim	for (p = dp->list; p; p = p->fts_link) {
153303231Sdim		if (IS_NOPRINT(p))
154303231Sdim			continue;
155303231Sdim		sp = p->fts_statp;
156303231Sdim		if (f_inode)
157314564Sdim			(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
158360784Sdim		if (f_size)
159360784Sdim			(void)printf("%*jd ",
160360784Sdim			    dp->s_block, howmany(sp->st_blocks, blocksize));
161303231Sdim		strmode(sp->st_mode, buf);
162303231Sdim		/*
163303231Sdim		 * Cache whether or not the filesystem supports ACL's to
164303231Sdim		 * avoid expensive syscalls. Try again when we change devices.
165303231Sdim		 */
166303231Sdim		if (haveacls || sp->st_dev != prevdev) {
167303231Sdim			aclmode(buf, p, &haveacls);
168303231Sdim			prevdev = sp->st_dev;
169314564Sdim		}
170303231Sdim		np = p->fts_pointer;
171344779Sdim		(void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
172344779Sdim		    sp->st_nlink, dp->s_user, np->user, dp->s_group,
173344779Sdim		    np->group);
174314564Sdim		if (f_flags)
175303231Sdim			(void)printf("%-*s ", dp->s_flags, np->flags);
176321369Sdim		if (f_label)
177303231Sdim			(void)printf("%-*s ", dp->s_label, np->label);
178303231Sdim		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
179303231Sdim			if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
180303231Sdim				(void)printf("%3d, 0x%08x ",
181314564Sdim				    major(sp->st_rdev),
182314564Sdim				    (u_int)minor(sp->st_rdev));
183314564Sdim			else
184303231Sdim				(void)printf("%3d, %3d ",
185303231Sdim				    major(sp->st_rdev), minor(sp->st_rdev));
186303231Sdim		else if (dp->bcfile)
187303231Sdim			(void)printf("%*s%*jd ",
188303231Sdim			    8 - dp->s_size, "", dp->s_size, sp->st_size);
189303231Sdim		else
190303231Sdim			printsize(dp->s_size, sp->st_size);
191321369Sdim		if (f_accesstime)
192303231Sdim			printtime(sp->st_atime);
193303231Sdim		else if (f_statustime)
194303231Sdim			printtime(sp->st_ctime);
195303231Sdim		else
196303231Sdim			printtime(sp->st_mtime);
197303231Sdim#ifdef COLORLS
198303231Sdim		if (f_color)
199303231Sdim			color_printed = colortype(sp->st_mode);
200303231Sdim#endif
201303231Sdim		(void)printname(p->fts_name);
202303231Sdim#ifdef COLORLS
203303231Sdim		if (f_color && color_printed)
204303231Sdim			endcolor(0);
205303231Sdim#endif
206303231Sdim		if (f_type)
207303231Sdim			(void)printtype(sp->st_mode);
208303231Sdim		if (S_ISLNK(sp->st_mode))
209303231Sdim			printlink(p);
210303231Sdim		(void)putchar('\n');
211303231Sdim	}
212303231Sdim}
213303231Sdim
214303231Sdimvoid
215303231Sdimprintstream(const DISPLAY *dp)
216303231Sdim{
217303231Sdim	FTSENT *p;
218303231Sdim	int chcnt;
219303231Sdim
220303231Sdim	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
221303231Sdim		if (p->fts_number == NO_PRINT)
222303231Sdim			continue;
223303231Sdim		/* XXX strlen does not take octal escapes into account. */
224303231Sdim		if (strlen(p->fts_name) + chcnt +
225303231Sdim		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
226303231Sdim			putchar('\n');
227303231Sdim			chcnt = 0;
228303231Sdim		}
229303231Sdim		chcnt += printaname(p, dp->s_inode, dp->s_block);
230303231Sdim		if (p->fts_link) {
231303231Sdim			printf(", ");
232303231Sdim			chcnt += 2;
233303231Sdim		}
234303231Sdim	}
235303231Sdim	if (chcnt)
236303231Sdim		putchar('\n');
237303231Sdim}
238303231Sdim
239303231Sdimvoid
240303231Sdimprintcol(const DISPLAY *dp)
241303231Sdim{
242303231Sdim	static FTSENT **array;
243303231Sdim	static int lastentries = -1;
244303231Sdim	FTSENT *p;
245321369Sdim	FTSENT **narray;
246321369Sdim	int base;
247321369Sdim	int chcnt;
248321369Sdim	int cnt;
249321369Sdim	int col;
250321369Sdim	int colwidth;
251321369Sdim	int endcol;
252321369Sdim	int num;
253303231Sdim	int numcols;
254327952Sdim	int numrows;
255327952Sdim	int row;
256327952Sdim	int tabwidth;
257327952Sdim
258327952Sdim	if (f_notabs)
259327952Sdim		tabwidth = 1;
260327952Sdim	else
261327952Sdim		tabwidth = 8;
262327952Sdim
263327952Sdim	/*
264303231Sdim	 * Have to do random access in the linked list -- build a table
265314564Sdim	 * of pointers.
266327952Sdim	 */
267327952Sdim	if (dp->entries > lastentries) {
268303231Sdim		if ((narray =
269303231Sdim		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
270303231Sdim			warn(NULL);
271303231Sdim			printscol(dp);
272314564Sdim			return;
273314564Sdim		}
274314564Sdim		lastentries = dp->entries;
275303231Sdim		array = narray;
276303231Sdim	}
277303231Sdim	for (p = dp->list, num = 0; p; p = p->fts_link)
278314564Sdim		if (p->fts_number != NO_PRINT)
279314564Sdim			array[num++] = p;
280314564Sdim
281314564Sdim	colwidth = dp->maxlen;
282314564Sdim	if (f_inode)
283303231Sdim		colwidth += dp->s_inode + 1;
284303231Sdim	if (f_size)
285303231Sdim		colwidth += dp->s_block + 1;
286303231Sdim	if (f_type)
287303231Sdim		colwidth += 1;
288303231Sdim
289314564Sdim	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
290314564Sdim	if (termwidth < 2 * colwidth) {
291303231Sdim		printscol(dp);
292303231Sdim		return;
293303231Sdim	}
294303231Sdim	numcols = termwidth / colwidth;
295303231Sdim	numrows = num / numcols;
296303231Sdim	if (num % numcols)
297314564Sdim		++numrows;
298314564Sdim
299314564Sdim	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
300314564Sdim	    (f_longform || f_size)) {
301303231Sdim		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
302303231Sdim	}
303303231Sdim
304303231Sdim	base = 0;
305303231Sdim	for (row = 0; row < numrows; ++row) {
306303231Sdim		endcol = colwidth;
307303231Sdim		if (!f_sortacross)
308303231Sdim			base = row;
309303231Sdim		for (col = 0, chcnt = 0; col < numcols; ++col) {
310341825Sdim			chcnt += printaname(array[base], dp->s_inode,
311341825Sdim			    dp->s_block);
312341825Sdim			if (f_sortacross)
313341825Sdim				base++;
314341825Sdim			else
315341825Sdim				base += numrows;
316341825Sdim			if (base >= num)
317341825Sdim				break;
318321369Sdim			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
319303231Sdim			    <= endcol) {
320303231Sdim				if (f_sortacross && col + 1 >= numcols)
321314564Sdim					break;
322314564Sdim				(void)putchar(f_notabs ? ' ' : '\t');
323314564Sdim				chcnt = cnt;
324344779Sdim			}
325314564Sdim			endcol += colwidth;
326314564Sdim		}
327314564Sdim		(void)putchar('\n');
328303231Sdim	}
329303231Sdim}
330303231Sdim
331360784Sdim/*
332303231Sdim * print [inode] [size] name
333303231Sdim * return # of characters printed, no trailing characters.
334303231Sdim */
335303231Sdimstatic int
336303231Sdimprintaname(const FTSENT *p, u_long inodefield, u_long sizefield)
337303231Sdim{
338303231Sdim	struct stat *sp;
339303231Sdim	int chcnt;
340303231Sdim#ifdef COLORLS
341303231Sdim	int color_printed = 0;
342303231Sdim#endif
343303231Sdim
344303231Sdim	sp = p->fts_statp;
345303231Sdim	chcnt = 0;
346344779Sdim	if (f_inode)
347344779Sdim		chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
348321369Sdim	if (f_size)
349321369Sdim		chcnt += printf("%*jd ",
350321369Sdim		    (int)sizefield, howmany(sp->st_blocks, blocksize));
351321369Sdim#ifdef COLORLS
352303231Sdim	if (f_color)
353303231Sdim		color_printed = colortype(sp->st_mode);
354303231Sdim#endif
355303231Sdim	chcnt += printname(p->fts_name);
356303231Sdim#ifdef COLORLS
357303231Sdim	if (f_color && color_printed)
358303231Sdim		endcolor(0);
359303231Sdim#endif
360303231Sdim	if (f_type)
361303231Sdim		chcnt += printtype(sp->st_mode);
362303231Sdim	return (chcnt);
363303231Sdim}
364303231Sdim
365303231Sdimstatic void
366303231Sdimprinttime(time_t ftime)
367303231Sdim{
368303231Sdim	char longstring[80];
369303231Sdim	static time_t now = 0;
370303231Sdim	const char *format;
371303231Sdim	static int d_first = -1;
372303231Sdim
373303231Sdim	if (d_first < 0)
374303231Sdim		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
375303231Sdim	if (now == 0)
376303231Sdim		now = time(NULL);
377303231Sdim
378303231Sdim#define	SIXMONTHS	((365 / 2) * 86400)
379303231Sdim	if (f_sectime)
380303231Sdim		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
381303231Sdim		format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
382303231Sdim	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
383303231Sdim		/* mmm dd hh:mm || dd mmm hh:mm */
384303231Sdim		format = d_first ? "%e %b %R " : "%b %e %R ";
385303231Sdim	else
386303231Sdim		/* mmm dd  yyyy || dd mmm  yyyy */
387303231Sdim		format = d_first ? "%e %b  %Y " : "%b %e  %Y ";
388303231Sdim	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
389303231Sdim	fputs(longstring, stdout);
390303231Sdim}
391303231Sdim
392303231Sdimstatic int
393303231Sdimprinttype(u_int mode)
394303231Sdim{
395303231Sdim
396303231Sdim	if (f_slash) {
397303231Sdim		if ((mode & S_IFMT) == S_IFDIR) {
398303231Sdim			(void)putchar('/');
399303231Sdim			return (1);
400303231Sdim		}
401303231Sdim		return (0);
402303231Sdim	}
403303231Sdim
404303231Sdim	switch (mode & S_IFMT) {
405303231Sdim	case S_IFDIR:
406303231Sdim		(void)putchar('/');
407303231Sdim		return (1);
408303231Sdim	case S_IFIFO:
409303231Sdim		(void)putchar('|');
410303231Sdim		return (1);
411303231Sdim	case S_IFLNK:
412303231Sdim		(void)putchar('@');
413303231Sdim		return (1);
414303231Sdim	case S_IFSOCK:
415303231Sdim		(void)putchar('=');
416303231Sdim		return (1);
417303231Sdim	case S_IFWHT:
418303231Sdim		(void)putchar('%');
419303231Sdim		return (1);
420303231Sdim	default:
421303231Sdim		break;
422303231Sdim	}
423303231Sdim	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
424303231Sdim		(void)putchar('*');
425303231Sdim		return (1);
426303231Sdim	}
427303231Sdim	return (0);
428303231Sdim}
429303231Sdim
430303231Sdim#ifdef COLORLS
431303231Sdimstatic int
432303231Sdimputch(int c)
433314564Sdim{
434321369Sdim	(void)putchar(c);
435303231Sdim	return 0;
436321369Sdim}
437303231Sdim
438303231Sdimstatic int
439341825Sdimwritech(int c)
440303231Sdim{
441321369Sdim	char tmp = (char)c;
442321369Sdim
443303231Sdim	(void)write(STDOUT_FILENO, &tmp, 1);
444321369Sdim	return 0;
445321369Sdim}
446303231Sdim
447303231Sdimstatic void
448321369Sdimprintcolor(Colors c)
449321369Sdim{
450321369Sdim	char *ansiseq;
451321369Sdim
452303231Sdim	if (colors[c].bold)
453303231Sdim		tputs(enter_bold, 1, putch);
454303231Sdim
455303231Sdim	if (colors[c].num[0] != -1) {
456303231Sdim		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
457314564Sdim		if (ansiseq)
458314564Sdim			tputs(ansiseq, 1, putch);
459314564Sdim	}
460303231Sdim	if (colors[c].num[1] != -1) {
461303231Sdim		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
462303231Sdim		if (ansiseq)
463314564Sdim			tputs(ansiseq, 1, putch);
464303231Sdim	}
465303231Sdim}
466314564Sdim
467314564Sdimstatic void
468314564Sdimendcolor(int sig)
469314564Sdim{
470314564Sdim	tputs(ansi_coloff, 1, sig ? writech : putch);
471303231Sdim	tputs(attrs_off, 1, sig ? writech : putch);
472303231Sdim}
473303231Sdim
474303231Sdimstatic int
475303231Sdimcolortype(mode_t mode)
476303231Sdim{
477303231Sdim	switch (mode & S_IFMT) {
478314564Sdim	case S_IFDIR:
479314564Sdim		if (mode & S_IWOTH)
480314564Sdim			if (mode & S_ISTXT)
481303231Sdim				printcolor(C_WSDIR);
482303231Sdim			else
483303231Sdim				printcolor(C_WDIR);
484303231Sdim		else
485303231Sdim			printcolor(C_DIR);
486303231Sdim		return (1);
487303231Sdim	case S_IFLNK:
488303231Sdim		printcolor(C_LNK);
489303231Sdim		return (1);
490303231Sdim	case S_IFSOCK:
491303231Sdim		printcolor(C_SOCK);
492303231Sdim		return (1);
493303231Sdim	case S_IFIFO:
494303231Sdim		printcolor(C_FIFO);
495303231Sdim		return (1);
496303231Sdim	case S_IFBLK:
497303231Sdim		printcolor(C_BLK);
498303231Sdim		return (1);
499303231Sdim	case S_IFCHR:
500303231Sdim		printcolor(C_CHR);
501303231Sdim		return (1);
502303231Sdim	default:;
503303231Sdim	}
504303231Sdim	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
505303231Sdim		if (mode & S_ISUID)
506303231Sdim			printcolor(C_SUID);
507303231Sdim		else if (mode & S_ISGID)
508303231Sdim			printcolor(C_SGID);
509303231Sdim		else
510303231Sdim			printcolor(C_EXEC);
511303231Sdim		return (1);
512303231Sdim	}
513303231Sdim	return (0);
514303231Sdim}
515303231Sdim
516303231Sdimvoid
517303231Sdimparsecolors(const char *cs)
518303231Sdim{
519303231Sdim	int i;
520303231Sdim	int j;
521303231Sdim	size_t len;
522303231Sdim	char c[2];
523341825Sdim	short legacy_warn = 0;
524303231Sdim
525314564Sdim	if (cs == NULL)
526314564Sdim		cs = "";	/* LSCOLORS not set */
527303231Sdim	len = strlen(cs);
528303231Sdim	for (i = 0; i < (int)C_NUMCOLORS; i++) {
529303231Sdim		colors[i].bold = 0;
530303231Sdim
531341825Sdim		if (len <= 2 * (size_t)i) {
532303231Sdim			c[0] = defcolors[2 * i];
533303231Sdim			c[1] = defcolors[2 * i + 1];
534303231Sdim		} else {
535303231Sdim			c[0] = cs[2 * i];
536303231Sdim			c[1] = cs[2 * i + 1];
537341825Sdim		}
538303231Sdim		for (j = 0; j < 2; j++) {
539303231Sdim			/* Legacy colours used 0-7 */
540303231Sdim			if (c[j] >= '0' && c[j] <= '7') {
541303231Sdim				colors[i].num[j] = c[j] - '0';
542303231Sdim				if (!legacy_warn) {
543341825Sdim					warnx("LSCOLORS should use "
544303231Sdim					    "characters a-h instead of 0-9 ("
545321369Sdim					    "see the manual page)");
546303231Sdim				}
547303231Sdim				legacy_warn = 1;
548341825Sdim			} else if (c[j] >= 'a' && c[j] <= 'h')
549303231Sdim				colors[i].num[j] = c[j] - 'a';
550303231Sdim			else if (c[j] >= 'A' && c[j] <= 'H') {
551360784Sdim				colors[i].num[j] = c[j] - 'A';
552303231Sdim				colors[i].bold = 1;
553314564Sdim			} else if (tolower((unsigned char)c[j]) == 'x')
554314564Sdim				colors[i].num[j] = -1;
555303231Sdim			else {
556303231Sdim				warnx("invalid character '%c' in LSCOLORS"
557303231Sdim				    " env var", c[j]);
558303231Sdim				colors[i].num[j] = -1;
559341825Sdim			}
560303231Sdim		}
561303231Sdim	}
562303231Sdim}
563303231Sdim
564303231Sdimvoid
565341825Sdimcolorquit(int sig)
566303231Sdim{
567303231Sdim	endcolor(sig);
568303231Sdim
569303231Sdim	(void)signal(sig, SIG_DFL);
570303231Sdim	(void)kill(getpid(), sig);
571341825Sdim}
572303231Sdim
573303231Sdim#endif /* COLORLS */
574303231Sdim
575303231Sdimstatic void
576303231Sdimprintlink(const FTSENT *p)
577341825Sdim{
578303231Sdim	int lnklen;
579303231Sdim	char name[MAXPATHLEN + 1];
580303231Sdim	char path[MAXPATHLEN + 1];
581303231Sdim
582303231Sdim	if (p->fts_level == FTS_ROOTLEVEL)
583303231Sdim		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
584303231Sdim	else
585303231Sdim		(void)snprintf(name, sizeof(name),
586303231Sdim		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
587303231Sdim	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
588303231Sdim		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
589303231Sdim		return;
590303231Sdim	}
591303231Sdim	path[lnklen] = '\0';
592303231Sdim	(void)printf(" -> ");
593303231Sdim	(void)printname(path);
594303231Sdim}
595303231Sdim
596314564Sdimstatic void
597314564Sdimprintsize(size_t width, off_t bytes)
598314564Sdim{
599303231Sdim
600303231Sdim	if (f_humanval) {
601303231Sdim		char buf[5];
602303231Sdim
603303231Sdim		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
604303231Sdim		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
605303231Sdim		(void)printf("%5s ", buf);
606303231Sdim	} else
607303231Sdim		(void)printf("%*jd ", (u_int)width, bytes);
608303231Sdim}
609303231Sdim
610303231Sdimstatic void
611303231Sdimaclmode(char *buf, const FTSENT *p, int *haveacls)
612303231Sdim{
613303231Sdim	char name[MAXPATHLEN + 1];
614303231Sdim	int entries, ret;
615303231Sdim	acl_t facl;
616303231Sdim	acl_entry_t ae;
617303231Sdim
618303231Sdim	/*
619303231Sdim	 * Add a + after the standard rwxrwxrwx mode if the file has an
620303231Sdim	 * extended ACL. strmode() reserves space at the end of the string.
621303231Sdim	 */
622303231Sdim	if (p->fts_level == FTS_ROOTLEVEL)
623303231Sdim		snprintf(name, sizeof(name), "%s", p->fts_name);
624303231Sdim	else
625303231Sdim		snprintf(name, sizeof(name), "%s/%s",
626303231Sdim		    p->fts_parent->fts_accpath, p->fts_name);
627303231Sdim	/*
628303231Sdim	 * We have no way to tell whether a symbolic link has an ACL since
629303231Sdim	 * pathconf() and acl_get_file() both follow them.
630303231Sdim	 */
631303231Sdim	if (S_ISLNK(p->fts_statp->st_mode)) {
632303231Sdim		*haveacls = 1;
633303231Sdim		return;
634303231Sdim	}
635303231Sdim	if ((ret = pathconf(name, _PC_ACL_EXTENDED)) <= 0) {
636303231Sdim		if (ret < 0 && errno != EINVAL)
637303231Sdim			warn("%s", name);
638303231Sdim		else
639303231Sdim			*haveacls = 0;
640321369Sdim		return;
641321369Sdim	}
642321369Sdim	*haveacls = 1;
643321369Sdim	if ((facl = acl_get_file(name, ACL_TYPE_ACCESS)) != NULL) {
644321369Sdim		if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 1) {
645321369Sdim			entries = 1;
646321369Sdim			while (acl_get_entry(facl, ACL_NEXT_ENTRY, &ae) == 1)
647321369Sdim				if (++entries > 3)
648321369Sdim					break;
649321369Sdim			/*
650321369Sdim			 * POSIX.1e requires that ACLs of type ACL_TYPE_ACCESS
651321369Sdim			 * must have at least three entries (owner, group,
652327952Sdim			 * and other). So anything with more than 3 ACLs looks
653327952Sdim			 * interesting to us.
654327952Sdim			 */
655327952Sdim			if (entries > 3)
656327952Sdim				buf[10] = '+';
657327952Sdim		}
658327952Sdim		acl_free(facl);
659327952Sdim	} else
660327952Sdim		warn("%s", name);
661327952Sdim}
662327952Sdim