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#include <sys/param.h>
36#include <sys/stat.h>
37#include <sys/acl.h>
38
39#include <err.h>
40#include <errno.h>
41#include <fts.h>
42#include <langinfo.h>
43#include <libutil.h>
44#include <limits.h>
45#include <stdio.h>
46#include <stdint.h>
47#include <stdlib.h>
48#include <string.h>
49#include <time.h>
50#include <unistd.h>
51#include <wchar.h>
52#ifdef COLORLS
53#include <ctype.h>
54#include <termcap.h>
55#include <signal.h>
56#endif
57
58#include "ls.h"
59#include "extern.h"
60
61static int	printaname(const FTSENT *, u_long, u_long);
62static void	printdev(size_t, dev_t);
63static void	printlink(const FTSENT *);
64static void	printtime(time_t);
65static int	printtype(u_int);
66static void	printsize(size_t, off_t);
67#ifdef COLORLS
68static void	endcolor_termcap(int);
69static void	endcolor_ansi(void);
70static void	endcolor(int);
71static int	colortype(mode_t);
72#endif
73static void	aclmode(char *, const FTSENT *);
74
75#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
76
77#ifdef COLORLS
78/* Most of these are taken from <sys/stat.h> */
79typedef enum Colors {
80	C_DIR,			/* directory */
81	C_LNK,			/* symbolic link */
82	C_SOCK,			/* socket */
83	C_FIFO,			/* pipe */
84	C_EXEC,			/* executable */
85	C_BLK,			/* block special */
86	C_CHR,			/* character special */
87	C_SUID,			/* setuid executable */
88	C_SGID,			/* setgid executable */
89	C_WSDIR,		/* directory writeble to others, with sticky
90				 * bit */
91	C_WDIR,			/* directory writeble to others, without
92				 * sticky bit */
93	C_NUMCOLORS		/* just a place-holder */
94} Colors;
95
96static const char *defcolors = "exfxcxdxbxegedabagacad";
97
98/* colors for file types */
99static struct {
100	int	num[2];
101	bool	bold;
102	bool	underline;
103} colors[C_NUMCOLORS];
104#endif
105
106static size_t padding_for_month[12];
107static size_t month_max_size = 0;
108
109void
110printscol(const DISPLAY *dp)
111{
112	FTSENT *p;
113
114	for (p = dp->list; p; p = p->fts_link) {
115		if (IS_NOPRINT(p))
116			continue;
117		(void)printaname(p, dp->s_inode, dp->s_block);
118		(void)putchar('\n');
119	}
120}
121
122/*
123 * print name in current style
124 */
125int
126printname(const char *name)
127{
128	if (f_octal || f_octal_escape)
129		return prn_octal(name);
130	else if (f_nonprint)
131		return prn_printable(name);
132	else
133		return prn_normal(name);
134}
135
136static const char *
137get_abmon(int mon)
138{
139
140	switch (mon) {
141	case 0: return (nl_langinfo(ABMON_1));
142	case 1: return (nl_langinfo(ABMON_2));
143	case 2: return (nl_langinfo(ABMON_3));
144	case 3: return (nl_langinfo(ABMON_4));
145	case 4: return (nl_langinfo(ABMON_5));
146	case 5: return (nl_langinfo(ABMON_6));
147	case 6: return (nl_langinfo(ABMON_7));
148	case 7: return (nl_langinfo(ABMON_8));
149	case 8: return (nl_langinfo(ABMON_9));
150	case 9: return (nl_langinfo(ABMON_10));
151	case 10: return (nl_langinfo(ABMON_11));
152	case 11: return (nl_langinfo(ABMON_12));
153	}
154
155	/* should never happen */
156	abort();
157}
158
159static size_t
160mbswidth(const char *month)
161{
162	wchar_t wc;
163	size_t width, donelen, clen, w;
164
165	width = donelen = 0;
166	while ((clen = mbrtowc(&wc, month + donelen, MB_LEN_MAX, NULL)) != 0) {
167		if (clen == (size_t)-1 || clen == (size_t)-2)
168			return (-1);
169		donelen += clen;
170		if ((w = wcwidth(wc)) == (size_t)-1)
171			return (-1);
172		width += w;
173	}
174
175	return (width);
176}
177
178static void
179compute_abbreviated_month_size(void)
180{
181	int i;
182	size_t width;
183	size_t months_width[12];
184
185	for (i = 0; i < 12; i++) {
186		width = mbswidth(get_abmon(i));
187		if (width == (size_t)-1) {
188			month_max_size = -1;
189			return;
190		}
191		months_width[i] = width;
192		if (width > month_max_size)
193			month_max_size = width;
194	}
195
196	for (i = 0; i < 12; i++)
197		padding_for_month[i] = month_max_size - months_width[i];
198}
199
200void
201printlong(const DISPLAY *dp)
202{
203	struct stat *sp;
204	FTSENT *p;
205	NAMES *np;
206	char buf[20];
207#ifdef COLORLS
208	int color_printed = 0;
209#endif
210
211	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
212	    (f_longform || f_size)) {
213		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
214	}
215
216	for (p = dp->list; p; p = p->fts_link) {
217		if (IS_NOPRINT(p))
218			continue;
219		sp = p->fts_statp;
220		if (f_inode)
221			(void)printf("%*ju ",
222			    dp->s_inode, (uintmax_t)sp->st_ino);
223		if (f_size)
224			(void)printf("%*jd ",
225			    dp->s_block, howmany(sp->st_blocks, blocksize));
226		strmode(sp->st_mode, buf);
227		aclmode(buf, p);
228		np = p->fts_pointer;
229		(void)printf("%s %*ju ", buf, dp->s_nlink,
230		    (uintmax_t)sp->st_nlink);
231		if (!f_sowner)
232			(void)printf("%-*s ", dp->s_user, np->user);
233		(void)printf("%-*s ", dp->s_group, np->group);
234		if (f_flags)
235			(void)printf("%-*s ", dp->s_flags, np->flags);
236		if (f_label)
237			(void)printf("%-*s ", dp->s_label, np->label);
238		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
239			printdev(dp->s_size, sp->st_rdev);
240		else
241			printsize(dp->s_size, sp->st_size);
242		if (f_accesstime)
243			printtime(sp->st_atime);
244		else if (f_birthtime)
245			printtime(sp->st_birthtime);
246		else if (f_statustime)
247			printtime(sp->st_ctime);
248		else
249			printtime(sp->st_mtime);
250#ifdef COLORLS
251		if (f_color)
252			color_printed = colortype(sp->st_mode);
253#endif
254		(void)printname(p->fts_name);
255#ifdef COLORLS
256		if (f_color && color_printed)
257			endcolor(0);
258#endif
259		if (f_type)
260			(void)printtype(sp->st_mode);
261		if (S_ISLNK(sp->st_mode))
262			printlink(p);
263		(void)putchar('\n');
264	}
265}
266
267void
268printstream(const DISPLAY *dp)
269{
270	FTSENT *p;
271	int chcnt;
272
273	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
274		if (p->fts_number == NO_PRINT)
275			continue;
276		/* XXX strlen does not take octal escapes into account. */
277		if (strlen(p->fts_name) + chcnt +
278		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
279			putchar('\n');
280			chcnt = 0;
281		}
282		chcnt += printaname(p, dp->s_inode, dp->s_block);
283		if (p->fts_link) {
284			printf(", ");
285			chcnt += 2;
286		}
287	}
288	if (chcnt)
289		putchar('\n');
290}
291
292void
293printcol(const DISPLAY *dp)
294{
295	static FTSENT **array;
296	static int lastentries = -1;
297	FTSENT *p;
298	FTSENT **narray;
299	int base;
300	int chcnt;
301	int cnt;
302	int col;
303	int colwidth;
304	int endcol;
305	int num;
306	int numcols;
307	int numrows;
308	int row;
309	int tabwidth;
310
311	if (f_notabs)
312		tabwidth = 1;
313	else
314		tabwidth = 8;
315
316	/*
317	 * Have to do random access in the linked list -- build a table
318	 * of pointers.
319	 */
320	if (dp->entries > lastentries) {
321		if ((narray =
322		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
323			warn(NULL);
324			printscol(dp);
325			return;
326		}
327		lastentries = dp->entries;
328		array = narray;
329	}
330	for (p = dp->list, num = 0; p; p = p->fts_link)
331		if (p->fts_number != NO_PRINT)
332			array[num++] = p;
333
334	colwidth = dp->maxlen;
335	if (f_inode)
336		colwidth += dp->s_inode + 1;
337	if (f_size)
338		colwidth += dp->s_block + 1;
339	if (f_type)
340		colwidth += 1;
341
342	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
343	if (termwidth < 2 * colwidth) {
344		printscol(dp);
345		return;
346	}
347	numcols = termwidth / colwidth;
348	numrows = num / numcols;
349	if (num % numcols)
350		++numrows;
351
352	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
353	    (f_longform || f_size)) {
354		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
355	}
356
357	base = 0;
358	for (row = 0; row < numrows; ++row) {
359		endcol = colwidth;
360		if (!f_sortacross)
361			base = row;
362		for (col = 0, chcnt = 0; col < numcols; ++col) {
363			chcnt += printaname(array[base], dp->s_inode,
364			    dp->s_block);
365			if (f_sortacross)
366				base++;
367			else
368				base += numrows;
369			if (base >= num)
370				break;
371			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
372			    <= endcol) {
373				if (f_sortacross && col + 1 >= numcols)
374					break;
375				(void)putchar(f_notabs ? ' ' : '\t');
376				chcnt = cnt;
377			}
378			endcol += colwidth;
379		}
380		(void)putchar('\n');
381	}
382}
383
384/*
385 * print [inode] [size] name
386 * return # of characters printed, no trailing characters.
387 */
388static int
389printaname(const FTSENT *p, u_long inodefield, u_long sizefield)
390{
391	struct stat *sp;
392	int chcnt;
393#ifdef COLORLS
394	int color_printed = 0;
395#endif
396
397	sp = p->fts_statp;
398	chcnt = 0;
399	if (f_inode)
400		chcnt += printf("%*ju ",
401		    (int)inodefield, (uintmax_t)sp->st_ino);
402	if (f_size)
403		chcnt += printf("%*jd ",
404		    (int)sizefield, howmany(sp->st_blocks, blocksize));
405#ifdef COLORLS
406	if (f_color)
407		color_printed = colortype(sp->st_mode);
408#endif
409	chcnt += printname(p->fts_name);
410#ifdef COLORLS
411	if (f_color && color_printed)
412		endcolor(0);
413#endif
414	if (f_type)
415		chcnt += printtype(sp->st_mode);
416	return (chcnt);
417}
418
419/*
420 * Print device special file major and minor numbers.
421 */
422static void
423printdev(size_t width, dev_t dev)
424{
425
426	(void)printf("%#*jx ", (u_int)width, (uintmax_t)dev);
427}
428
429static void
430ls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm)
431{
432	char *posb, nfmt[BUFSIZ];
433	const char *format = fmt;
434
435	if ((posb = strstr(fmt, "%b")) != NULL) {
436		if (month_max_size == 0) {
437			compute_abbreviated_month_size();
438		}
439		if (month_max_size > 0 && tm != NULL) {
440			snprintf(nfmt, sizeof(nfmt),  "%.*s%s%*s%s",
441			    (int)(posb - fmt), fmt,
442			    get_abmon(tm->tm_mon),
443			    (int)padding_for_month[tm->tm_mon],
444			    "",
445			    posb + 2);
446			format = nfmt;
447		}
448	}
449	if (tm != NULL)
450		strftime(str, len, format, tm);
451	else
452		strlcpy(str, "bad date val", len);
453}
454
455static void
456printtime(time_t ftime)
457{
458	char longstring[80];
459	static time_t now = 0;
460	const char *format;
461	static int d_first = -1;
462
463	if (d_first < 0)
464		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
465	if (now == 0)
466		now = time(NULL);
467
468#define	SIXMONTHS	((365 / 2) * 86400)
469	if (f_timeformat)  /* user specified format */
470		format = f_timeformat;
471	else if (f_sectime)
472		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
473		format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
474	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
475		/* mmm dd hh:mm || dd mmm hh:mm */
476		format = d_first ? "%e %b %R" : "%b %e %R";
477	else
478		/* mmm dd  yyyy || dd mmm  yyyy */
479		format = d_first ? "%e %b  %Y" : "%b %e  %Y";
480	ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime));
481	fputs(longstring, stdout);
482	fputc(' ', stdout);
483}
484
485static int
486printtype(u_int mode)
487{
488
489	if (f_slash) {
490		if ((mode & S_IFMT) == S_IFDIR) {
491			(void)putchar('/');
492			return (1);
493		}
494		return (0);
495	}
496
497	switch (mode & S_IFMT) {
498	case S_IFDIR:
499		(void)putchar('/');
500		return (1);
501	case S_IFIFO:
502		(void)putchar('|');
503		return (1);
504	case S_IFLNK:
505		(void)putchar('@');
506		return (1);
507	case S_IFSOCK:
508		(void)putchar('=');
509		return (1);
510	case S_IFWHT:
511		(void)putchar('%');
512		return (1);
513	default:
514		break;
515	}
516	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
517		(void)putchar('*');
518		return (1);
519	}
520	return (0);
521}
522
523#ifdef COLORLS
524static int
525putch(int c)
526{
527	(void)putchar(c);
528	return 0;
529}
530
531static int
532writech(int c)
533{
534	char tmp = (char)c;
535
536	(void)write(STDOUT_FILENO, &tmp, 1);
537	return 0;
538}
539
540static void
541printcolor_termcap(Colors c)
542{
543	char *ansiseq;
544
545	if (colors[c].bold)
546		tputs(enter_bold, 1, putch);
547	if (colors[c].underline)
548		tputs(enter_underline, 1, putch);
549
550	if (colors[c].num[0] != -1) {
551		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
552		if (ansiseq)
553			tputs(ansiseq, 1, putch);
554	}
555	if (colors[c].num[1] != -1) {
556		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
557		if (ansiseq)
558			tputs(ansiseq, 1, putch);
559	}
560}
561
562static void
563printcolor_ansi(Colors c)
564{
565
566	printf("\033[");
567
568	if (colors[c].bold)
569		printf("1");
570	if (colors[c].underline)
571		printf(";4");
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 = false;
670		colors[i].underline = false;
671
672		if (len <= 2 * (size_t)i) {
673			c[0] = defcolors[2 * i];
674			c[1] = defcolors[2 * i + 1];
675		} else {
676			c[0] = cs[2 * i];
677			c[1] = cs[2 * i + 1];
678		}
679		for (j = 0; j < 2; j++) {
680			/* Legacy colours used 0-7 */
681			if (c[j] >= '0' && c[j] <= '7') {
682				colors[i].num[j] = c[j] - '0';
683				if (!legacy_warn) {
684					warnx("LSCOLORS should use "
685					    "characters a-h instead of 0-9 ("
686					    "see the manual page)");
687				}
688				legacy_warn = 1;
689			} else if (c[j] >= 'a' && c[j] <= 'h')
690				colors[i].num[j] = c[j] - 'a';
691			else if (c[j] >= 'A' && c[j] <= 'H') {
692				colors[i].num[j] = c[j] - 'A';
693				if (j == 1)
694					colors[i].underline = true;
695				else
696					colors[i].bold = true;
697			} else if (tolower((unsigned char)c[j]) == 'x') {
698				if (j == 1 && c[j] == 'X')
699					colors[i].underline = true;
700				colors[i].num[j] = -1;
701			} else {
702				warnx("invalid character '%c' in LSCOLORS"
703				    " env var", c[j]);
704				colors[i].num[j] = -1;
705			}
706		}
707	}
708}
709
710void
711colorquit(int sig)
712{
713	endcolor(sig);
714
715	(void)signal(sig, SIG_DFL);
716	(void)kill(getpid(), sig);
717}
718
719#endif /* COLORLS */
720
721static void
722printlink(const FTSENT *p)
723{
724	int lnklen;
725	char name[MAXPATHLEN + 1];
726	char path[MAXPATHLEN + 1];
727
728	if (p->fts_level == FTS_ROOTLEVEL)
729		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
730	else
731		(void)snprintf(name, sizeof(name),
732		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
733	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
734		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
735		return;
736	}
737	path[lnklen] = '\0';
738	(void)printf(" -> ");
739	(void)printname(path);
740}
741
742static void
743printsize(size_t width, off_t bytes)
744{
745
746	if (f_humanval) {
747		/*
748		 * Reserve one space before the size and allocate room for
749		 * the trailing '\0'.
750		 */
751		char buf[HUMANVALSTR_LEN - 1 + 1];
752
753		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
754		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
755		(void)printf("%*s ", (u_int)width, buf);
756	} else if (f_thousands) {		/* with commas */
757		/* This format assignment needed to work round gcc bug. */
758		const char *format = "%*j'd ";
759		(void)printf(format, (u_int)width, bytes);
760	} else
761		(void)printf("%*jd ", (u_int)width, bytes);
762}
763
764/*
765 * Add a + after the standard rwxrwxrwx mode if the file has an
766 * ACL. strmode() reserves space at the end of the string.
767 */
768static void
769aclmode(char *buf, const FTSENT *p)
770{
771	char name[MAXPATHLEN + 1];
772	int ret, trivial;
773	static dev_t previous_dev = NODEV;
774	static int supports_acls = -1;
775	static int type = ACL_TYPE_ACCESS;
776	acl_t facl;
777
778	/*
779	 * XXX: ACLs are not supported on whiteouts and device files
780	 * residing on UFS.
781	 */
782	if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
783	    S_ISWHT(p->fts_statp->st_mode))
784		return;
785
786	if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
787		return;
788
789	if (p->fts_level == FTS_ROOTLEVEL)
790		snprintf(name, sizeof(name), "%s", p->fts_name);
791	else
792		snprintf(name, sizeof(name), "%s/%s",
793		    p->fts_parent->fts_accpath, p->fts_name);
794
795	if (previous_dev != p->fts_statp->st_dev) {
796		previous_dev = p->fts_statp->st_dev;
797		supports_acls = 0;
798
799		ret = lpathconf(name, _PC_ACL_NFS4);
800		if (ret > 0) {
801			type = ACL_TYPE_NFS4;
802			supports_acls = 1;
803		} else if (ret < 0 && errno != EINVAL) {
804			warn("%s", name);
805			return;
806		}
807		if (supports_acls == 0) {
808			ret = lpathconf(name, _PC_ACL_EXTENDED);
809			if (ret > 0) {
810				type = ACL_TYPE_ACCESS;
811				supports_acls = 1;
812			} else if (ret < 0 && errno != EINVAL) {
813				warn("%s", name);
814				return;
815			}
816		}
817	}
818	if (supports_acls == 0)
819		return;
820	facl = acl_get_link_np(name, type);
821	if (facl == NULL) {
822		warn("%s", name);
823		return;
824	}
825	if (acl_is_trivial_np(facl, &trivial)) {
826		acl_free(facl);
827		warn("%s", name);
828		return;
829	}
830	if (!trivial)
831		buf[10] = '+';
832	acl_free(facl);
833}
834