1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1989, 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Michael Fischbein.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#if 0
36#ifndef lint
37static char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
38#endif /* not lint */
39#endif
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD$");
42
43#include <sys/param.h>
44#include <sys/stat.h>
45#include <sys/acl.h>
46
47#include <err.h>
48#include <errno.h>
49#include <fts.h>
50#include <langinfo.h>
51#include <libutil.h>
52#include <limits.h>
53#include <stdio.h>
54#include <stdint.h>
55#include <stdlib.h>
56#include <string.h>
57#include <time.h>
58#include <unistd.h>
59#include <wchar.h>
60#ifdef COLORLS
61#include <ctype.h>
62#include <termcap.h>
63#include <signal.h>
64#endif
65
66#include "ls.h"
67#include "extern.h"
68
69static int	printaname(const FTSENT *, u_long, u_long);
70static void	printdev(size_t, dev_t);
71static void	printlink(const FTSENT *);
72static void	printtime(time_t);
73static int	printtype(u_int);
74static void	printsize(size_t, off_t);
75#ifdef COLORLS
76static void	endcolor_termcap(int);
77static void	endcolor_ansi(void);
78static void	endcolor(int);
79static int	colortype(mode_t);
80#endif
81static void	aclmode(char *, const FTSENT *);
82
83#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
84
85#ifdef COLORLS
86/* Most of these are taken from <sys/stat.h> */
87typedef enum Colors {
88	C_DIR,			/* directory */
89	C_LNK,			/* symbolic link */
90	C_SOCK,			/* socket */
91	C_FIFO,			/* pipe */
92	C_EXEC,			/* executable */
93	C_BLK,			/* block special */
94	C_CHR,			/* character special */
95	C_SUID,			/* setuid executable */
96	C_SGID,			/* setgid executable */
97	C_WSDIR,		/* directory writeble to others, with sticky
98				 * bit */
99	C_WDIR,			/* directory writeble to others, without
100				 * sticky bit */
101	C_NUMCOLORS		/* just a place-holder */
102} Colors;
103
104static const char *defcolors = "exfxcxdxbxegedabagacad";
105
106/* colors for file types */
107static struct {
108	int	num[2];
109	int	bold;
110} colors[C_NUMCOLORS];
111#endif
112
113static size_t padding_for_month[12];
114static size_t month_max_size = 0;
115
116void
117printscol(const DISPLAY *dp)
118{
119	FTSENT *p;
120
121	for (p = dp->list; p; p = p->fts_link) {
122		if (IS_NOPRINT(p))
123			continue;
124		(void)printaname(p, dp->s_inode, dp->s_block);
125		(void)putchar('\n');
126	}
127}
128
129/*
130 * print name in current style
131 */
132int
133printname(const char *name)
134{
135	if (f_octal || f_octal_escape)
136		return prn_octal(name);
137	else if (f_nonprint)
138		return prn_printable(name);
139	else
140		return prn_normal(name);
141}
142
143static const char *
144get_abmon(int mon)
145{
146
147	switch (mon) {
148	case 0: return (nl_langinfo(ABMON_1));
149	case 1: return (nl_langinfo(ABMON_2));
150	case 2: return (nl_langinfo(ABMON_3));
151	case 3: return (nl_langinfo(ABMON_4));
152	case 4: return (nl_langinfo(ABMON_5));
153	case 5: return (nl_langinfo(ABMON_6));
154	case 6: return (nl_langinfo(ABMON_7));
155	case 7: return (nl_langinfo(ABMON_8));
156	case 8: return (nl_langinfo(ABMON_9));
157	case 9: return (nl_langinfo(ABMON_10));
158	case 10: return (nl_langinfo(ABMON_11));
159	case 11: return (nl_langinfo(ABMON_12));
160	}
161
162	/* should never happen */
163	abort();
164}
165
166static size_t
167mbswidth(const char *month)
168{
169	wchar_t wc;
170	size_t width, donelen, clen, w;
171
172	width = donelen = 0;
173	while ((clen = mbrtowc(&wc, month + donelen, MB_LEN_MAX, NULL)) != 0) {
174		if (clen == (size_t)-1 || clen == (size_t)-2)
175			return (-1);
176		donelen += clen;
177		if ((w = wcwidth(wc)) == (size_t)-1)
178			return (-1);
179		width += w;
180	}
181
182	return (width);
183}
184
185static void
186compute_abbreviated_month_size(void)
187{
188	int i;
189	size_t width;
190	size_t months_width[12];
191
192	for (i = 0; i < 12; i++) {
193		width = mbswidth(get_abmon(i));
194		if (width == (size_t)-1) {
195			month_max_size = -1;
196			return;
197		}
198		months_width[i] = width;
199		if (width > month_max_size)
200			month_max_size = width;
201	}
202
203	for (i = 0; i < 12; i++)
204		padding_for_month[i] = month_max_size - months_width[i];
205}
206
207void
208printlong(const DISPLAY *dp)
209{
210	struct stat *sp;
211	FTSENT *p;
212	NAMES *np;
213	char buf[20];
214#ifdef COLORLS
215	int color_printed = 0;
216#endif
217
218	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
219	    (f_longform || f_size)) {
220		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
221	}
222
223	for (p = dp->list; p; p = p->fts_link) {
224		if (IS_NOPRINT(p))
225			continue;
226		sp = p->fts_statp;
227		if (f_inode)
228			(void)printf("%*ju ",
229			    dp->s_inode, (uintmax_t)sp->st_ino);
230		if (f_size)
231			(void)printf("%*jd ",
232			    dp->s_block, howmany(sp->st_blocks, blocksize));
233		strmode(sp->st_mode, buf);
234		aclmode(buf, p);
235		np = p->fts_pointer;
236		(void)printf("%s %*ju %-*s  %-*s  ", buf, dp->s_nlink,
237		    (uintmax_t)sp->st_nlink, dp->s_user, np->user, dp->s_group,
238		    np->group);
239		if (f_flags)
240			(void)printf("%-*s ", dp->s_flags, np->flags);
241		if (f_label)
242			(void)printf("%-*s ", dp->s_label, np->label);
243		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
244			printdev(dp->s_size, sp->st_rdev);
245		else
246			printsize(dp->s_size, sp->st_size);
247		if (f_accesstime)
248			printtime(sp->st_atime);
249		else if (f_birthtime)
250			printtime(sp->st_birthtime);
251		else if (f_statustime)
252			printtime(sp->st_ctime);
253		else
254			printtime(sp->st_mtime);
255#ifdef COLORLS
256		if (f_color)
257			color_printed = colortype(sp->st_mode);
258#endif
259		(void)printname(p->fts_name);
260#ifdef COLORLS
261		if (f_color && color_printed)
262			endcolor(0);
263#endif
264		if (f_type)
265			(void)printtype(sp->st_mode);
266		if (S_ISLNK(sp->st_mode))
267			printlink(p);
268		(void)putchar('\n');
269	}
270}
271
272void
273printstream(const DISPLAY *dp)
274{
275	FTSENT *p;
276	int chcnt;
277
278	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
279		if (p->fts_number == NO_PRINT)
280			continue;
281		/* XXX strlen does not take octal escapes into account. */
282		if (strlen(p->fts_name) + chcnt +
283		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
284			putchar('\n');
285			chcnt = 0;
286		}
287		chcnt += printaname(p, dp->s_inode, dp->s_block);
288		if (p->fts_link) {
289			printf(", ");
290			chcnt += 2;
291		}
292	}
293	if (chcnt)
294		putchar('\n');
295}
296
297void
298printcol(const DISPLAY *dp)
299{
300	static FTSENT **array;
301	static int lastentries = -1;
302	FTSENT *p;
303	FTSENT **narray;
304	int base;
305	int chcnt;
306	int cnt;
307	int col;
308	int colwidth;
309	int endcol;
310	int num;
311	int numcols;
312	int numrows;
313	int row;
314	int tabwidth;
315
316	if (f_notabs)
317		tabwidth = 1;
318	else
319		tabwidth = 8;
320
321	/*
322	 * Have to do random access in the linked list -- build a table
323	 * of pointers.
324	 */
325	if (dp->entries > lastentries) {
326		if ((narray =
327		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
328			warn(NULL);
329			printscol(dp);
330			return;
331		}
332		lastentries = dp->entries;
333		array = narray;
334	}
335	for (p = dp->list, num = 0; p; p = p->fts_link)
336		if (p->fts_number != NO_PRINT)
337			array[num++] = p;
338
339	colwidth = dp->maxlen;
340	if (f_inode)
341		colwidth += dp->s_inode + 1;
342	if (f_size)
343		colwidth += dp->s_block + 1;
344	if (f_type)
345		colwidth += 1;
346
347	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
348	if (termwidth < 2 * colwidth) {
349		printscol(dp);
350		return;
351	}
352	numcols = termwidth / colwidth;
353	numrows = num / numcols;
354	if (num % numcols)
355		++numrows;
356
357	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
358	    (f_longform || f_size)) {
359		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
360	}
361
362	base = 0;
363	for (row = 0; row < numrows; ++row) {
364		endcol = colwidth;
365		if (!f_sortacross)
366			base = row;
367		for (col = 0, chcnt = 0; col < numcols; ++col) {
368			chcnt += printaname(array[base], dp->s_inode,
369			    dp->s_block);
370			if (f_sortacross)
371				base++;
372			else
373				base += numrows;
374			if (base >= num)
375				break;
376			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
377			    <= endcol) {
378				if (f_sortacross && col + 1 >= numcols)
379					break;
380				(void)putchar(f_notabs ? ' ' : '\t');
381				chcnt = cnt;
382			}
383			endcol += colwidth;
384		}
385		(void)putchar('\n');
386	}
387}
388
389/*
390 * print [inode] [size] name
391 * return # of characters printed, no trailing characters.
392 */
393static int
394printaname(const FTSENT *p, u_long inodefield, u_long sizefield)
395{
396	struct stat *sp;
397	int chcnt;
398#ifdef COLORLS
399	int color_printed = 0;
400#endif
401
402	sp = p->fts_statp;
403	chcnt = 0;
404	if (f_inode)
405		chcnt += printf("%*ju ",
406		    (int)inodefield, (uintmax_t)sp->st_ino);
407	if (f_size)
408		chcnt += printf("%*jd ",
409		    (int)sizefield, howmany(sp->st_blocks, blocksize));
410#ifdef COLORLS
411	if (f_color)
412		color_printed = colortype(sp->st_mode);
413#endif
414	chcnt += printname(p->fts_name);
415#ifdef COLORLS
416	if (f_color && color_printed)
417		endcolor(0);
418#endif
419	if (f_type)
420		chcnt += printtype(sp->st_mode);
421	return (chcnt);
422}
423
424/*
425 * Print device special file major and minor numbers.
426 */
427static void
428printdev(size_t width, dev_t dev)
429{
430
431	(void)printf("%#*jx ", (u_int)width, (uintmax_t)dev);
432}
433
434static size_t
435ls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm)
436{
437	char *posb, nfmt[BUFSIZ];
438	const char *format = fmt;
439	size_t ret;
440
441	if ((posb = strstr(fmt, "%b")) != NULL) {
442		if (month_max_size == 0) {
443			compute_abbreviated_month_size();
444		}
445		if (month_max_size > 0) {
446			snprintf(nfmt, sizeof(nfmt),  "%.*s%s%*s%s",
447			    (int)(posb - fmt), fmt,
448			    get_abmon(tm->tm_mon),
449			    (int)padding_for_month[tm->tm_mon],
450			    "",
451			    posb + 2);
452			format = nfmt;
453		}
454	}
455	ret = strftime(str, len, format, tm);
456	return (ret);
457}
458
459static void
460printtime(time_t ftime)
461{
462	char longstring[80];
463	static time_t now = 0;
464	const char *format;
465	static int d_first = -1;
466
467	if (d_first < 0)
468		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
469	if (now == 0)
470		now = time(NULL);
471
472#define	SIXMONTHS	((365 / 2) * 86400)
473	if (f_timeformat)  /* user specified format */
474		format = f_timeformat;
475	else if (f_sectime)
476		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
477		format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
478	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
479		/* mmm dd hh:mm || dd mmm hh:mm */
480		format = d_first ? "%e %b %R" : "%b %e %R";
481	else
482		/* mmm dd  yyyy || dd mmm  yyyy */
483		format = d_first ? "%e %b  %Y" : "%b %e  %Y";
484	ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime));
485	fputs(longstring, stdout);
486	fputc(' ', stdout);
487}
488
489static int
490printtype(u_int mode)
491{
492
493	if (f_slash) {
494		if ((mode & S_IFMT) == S_IFDIR) {
495			(void)putchar('/');
496			return (1);
497		}
498		return (0);
499	}
500
501	switch (mode & S_IFMT) {
502	case S_IFDIR:
503		(void)putchar('/');
504		return (1);
505	case S_IFIFO:
506		(void)putchar('|');
507		return (1);
508	case S_IFLNK:
509		(void)putchar('@');
510		return (1);
511	case S_IFSOCK:
512		(void)putchar('=');
513		return (1);
514	case S_IFWHT:
515		(void)putchar('%');
516		return (1);
517	default:
518		break;
519	}
520	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
521		(void)putchar('*');
522		return (1);
523	}
524	return (0);
525}
526
527#ifdef COLORLS
528static int
529putch(int c)
530{
531	(void)putchar(c);
532	return 0;
533}
534
535static int
536writech(int c)
537{
538	char tmp = (char)c;
539
540	(void)write(STDOUT_FILENO, &tmp, 1);
541	return 0;
542}
543
544static void
545printcolor_termcap(Colors c)
546{
547	char *ansiseq;
548
549	if (colors[c].bold)
550		tputs(enter_bold, 1, putch);
551
552	if (colors[c].num[0] != -1) {
553		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
554		if (ansiseq)
555			tputs(ansiseq, 1, putch);
556	}
557	if (colors[c].num[1] != -1) {
558		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
559		if (ansiseq)
560			tputs(ansiseq, 1, putch);
561	}
562}
563
564static void
565printcolor_ansi(Colors c)
566{
567
568	printf("\033[");
569
570	if (colors[c].bold)
571		printf("1");
572	if (colors[c].num[0] != -1)
573		printf(";3%d", colors[c].num[0]);
574	if (colors[c].num[1] != -1)
575		printf(";4%d", colors[c].num[1]);
576	printf("m");
577}
578
579static void
580printcolor(Colors c)
581{
582
583	if (explicitansi)
584		printcolor_ansi(c);
585	else
586		printcolor_termcap(c);
587}
588
589static void
590endcolor_termcap(int sig)
591{
592
593	tputs(ansi_coloff, 1, sig ? writech : putch);
594	tputs(attrs_off, 1, sig ? writech : putch);
595}
596
597static void
598endcolor_ansi(void)
599{
600
601	printf("\33[m");
602}
603
604static void
605endcolor(int sig)
606{
607
608	if (explicitansi)
609		endcolor_ansi();
610	else
611		endcolor_termcap(sig);
612}
613
614static int
615colortype(mode_t mode)
616{
617	switch (mode & S_IFMT) {
618	case S_IFDIR:
619		if (mode & S_IWOTH)
620			if (mode & S_ISTXT)
621				printcolor(C_WSDIR);
622			else
623				printcolor(C_WDIR);
624		else
625			printcolor(C_DIR);
626		return (1);
627	case S_IFLNK:
628		printcolor(C_LNK);
629		return (1);
630	case S_IFSOCK:
631		printcolor(C_SOCK);
632		return (1);
633	case S_IFIFO:
634		printcolor(C_FIFO);
635		return (1);
636	case S_IFBLK:
637		printcolor(C_BLK);
638		return (1);
639	case S_IFCHR:
640		printcolor(C_CHR);
641		return (1);
642	default:;
643	}
644	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
645		if (mode & S_ISUID)
646			printcolor(C_SUID);
647		else if (mode & S_ISGID)
648			printcolor(C_SGID);
649		else
650			printcolor(C_EXEC);
651		return (1);
652	}
653	return (0);
654}
655
656void
657parsecolors(const char *cs)
658{
659	int i;
660	int j;
661	size_t len;
662	char c[2];
663	short legacy_warn = 0;
664
665	if (cs == NULL)
666		cs = "";	/* LSCOLORS not set */
667	len = strlen(cs);
668	for (i = 0; i < (int)C_NUMCOLORS; i++) {
669		colors[i].bold = 0;
670
671		if (len <= 2 * (size_t)i) {
672			c[0] = defcolors[2 * i];
673			c[1] = defcolors[2 * i + 1];
674		} else {
675			c[0] = cs[2 * i];
676			c[1] = cs[2 * i + 1];
677		}
678		for (j = 0; j < 2; j++) {
679			/* Legacy colours used 0-7 */
680			if (c[j] >= '0' && c[j] <= '7') {
681				colors[i].num[j] = c[j] - '0';
682				if (!legacy_warn) {
683					warnx("LSCOLORS should use "
684					    "characters a-h instead of 0-9 ("
685					    "see the manual page)");
686				}
687				legacy_warn = 1;
688			} else if (c[j] >= 'a' && c[j] <= 'h')
689				colors[i].num[j] = c[j] - 'a';
690			else if (c[j] >= 'A' && c[j] <= 'H') {
691				colors[i].num[j] = c[j] - 'A';
692				colors[i].bold = 1;
693			} else if (tolower((unsigned char)c[j]) == 'x')
694				colors[i].num[j] = -1;
695			else {
696				warnx("invalid character '%c' in LSCOLORS"
697				    " env var", c[j]);
698				colors[i].num[j] = -1;
699			}
700		}
701	}
702}
703
704void
705colorquit(int sig)
706{
707	endcolor(sig);
708
709	(void)signal(sig, SIG_DFL);
710	(void)kill(getpid(), sig);
711}
712
713#endif /* COLORLS */
714
715static void
716printlink(const FTSENT *p)
717{
718	int lnklen;
719	char name[MAXPATHLEN + 1];
720	char path[MAXPATHLEN + 1];
721
722	if (p->fts_level == FTS_ROOTLEVEL)
723		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
724	else
725		(void)snprintf(name, sizeof(name),
726		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
727	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
728		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
729		return;
730	}
731	path[lnklen] = '\0';
732	(void)printf(" -> ");
733	(void)printname(path);
734}
735
736static void
737printsize(size_t width, off_t bytes)
738{
739
740	if (f_humanval) {
741		/*
742		 * Reserve one space before the size and allocate room for
743		 * the trailing '\0'.
744		 */
745		char buf[HUMANVALSTR_LEN - 1 + 1];
746
747		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
748		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
749		(void)printf("%*s ", (u_int)width, buf);
750	} else if (f_thousands) {		/* with commas */
751		/* This format assignment needed to work round gcc bug. */
752		const char *format = "%*j'd ";
753		(void)printf(format, (u_int)width, bytes);
754	} else
755		(void)printf("%*jd ", (u_int)width, bytes);
756}
757
758/*
759 * Add a + after the standard rwxrwxrwx mode if the file has an
760 * ACL. strmode() reserves space at the end of the string.
761 */
762static void
763aclmode(char *buf, const FTSENT *p)
764{
765	char name[MAXPATHLEN + 1];
766	int ret, trivial;
767	static dev_t previous_dev = NODEV;
768	static int supports_acls = -1;
769	static int type = ACL_TYPE_ACCESS;
770	acl_t facl;
771
772	/*
773	 * XXX: ACLs are not supported on whiteouts and device files
774	 * residing on UFS.
775	 */
776	if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
777	    S_ISWHT(p->fts_statp->st_mode))
778		return;
779
780	if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
781		return;
782
783	if (p->fts_level == FTS_ROOTLEVEL)
784		snprintf(name, sizeof(name), "%s", p->fts_name);
785	else
786		snprintf(name, sizeof(name), "%s/%s",
787		    p->fts_parent->fts_accpath, p->fts_name);
788
789	if (previous_dev != p->fts_statp->st_dev) {
790		previous_dev = p->fts_statp->st_dev;
791		supports_acls = 0;
792
793		ret = lpathconf(name, _PC_ACL_NFS4);
794		if (ret > 0) {
795			type = ACL_TYPE_NFS4;
796			supports_acls = 1;
797		} else if (ret < 0 && errno != EINVAL) {
798			warn("%s", name);
799			return;
800		}
801		if (supports_acls == 0) {
802			ret = lpathconf(name, _PC_ACL_EXTENDED);
803			if (ret > 0) {
804				type = ACL_TYPE_ACCESS;
805				supports_acls = 1;
806			} else if (ret < 0 && errno != EINVAL) {
807				warn("%s", name);
808				return;
809			}
810		}
811	}
812	if (supports_acls == 0)
813		return;
814	facl = acl_get_link_np(name, type);
815	if (facl == NULL) {
816		warn("%s", name);
817		return;
818	}
819	if (acl_is_trivial_np(facl, &trivial)) {
820		acl_free(facl);
821		warn("%s", name);
822		return;
823	}
824	if (!trivial)
825		buf[10] = '+';
826	acl_free(facl);
827}
828