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: stable/11/bin/ls/print.c 314129 2017-02-23 07:42:49Z kib $");
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>
50291959Sbapt#include <limits.h>
511556Srgrimes#include <stdio.h>
52225847Sed#include <stdint.h>
531556Srgrimes#include <stdlib.h>
541556Srgrimes#include <string.h>
5591212Sbde#include <time.h>
561556Srgrimes#include <unistd.h>
57291959Sbapt#include <wchar.h>
5861294Sache#ifdef COLORLS
5961294Sache#include <ctype.h>
6061294Sache#include <termcap.h>
6161294Sache#include <signal.h>
6261294Sache#endif
63284198Smarcel#include <libxo/xo.h>
641556Srgrimes
651556Srgrimes#include "ls.h"
661556Srgrimes#include "extern.h"
671556Srgrimes
68114583Smarkmstatic int	printaname(const FTSENT *, u_long, u_long);
69202945Sjhstatic void	printdev(size_t, dev_t);
70105780Smarkmstatic void	printlink(const FTSENT *);
71284198Smarcelstatic void	printtime(const char *, time_t);
7290110Simpstatic int	printtype(u_int);
73284198Smarcelstatic void	printsize(const char *, size_t, off_t);
7461321Sache#ifdef COLORLS
7590110Simpstatic void	endcolor(int);
7690110Simpstatic int	colortype(mode_t);
7761321Sache#endif
78196712Straszstatic void	aclmode(char *, const FTSENT *);
791556Srgrimes
801556Srgrimes#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
811556Srgrimes
8261268Sjoe#ifdef COLORLS
8361178Sjoe/* Most of these are taken from <sys/stat.h> */
8461178Sjoetypedef enum Colors {
8588602Sjoe	C_DIR,			/* directory */
8688602Sjoe	C_LNK,			/* symbolic link */
8788602Sjoe	C_SOCK,			/* socket */
8888602Sjoe	C_FIFO,			/* pipe */
8988602Sjoe	C_EXEC,			/* executable */
9088602Sjoe	C_BLK,			/* block special */
9188602Sjoe	C_CHR,			/* character special */
9288602Sjoe	C_SUID,			/* setuid executable */
9388602Sjoe	C_SGID,			/* setgid executable */
9488602Sjoe	C_WSDIR,		/* directory writeble to others, with sticky
9588602Sjoe				 * bit */
9688602Sjoe	C_WDIR,			/* directory writeble to others, without
9788602Sjoe				 * sticky bit */
9888602Sjoe	C_NUMCOLORS		/* just a place-holder */
9988586Sjoe} Colors;
10061178Sjoe
10190150Smarkmstatic const char *defcolors = "exfxcxdxbxegedabagacad";
10261178Sjoe
10388583Sjoe/* colors for file types */
10488583Sjoestatic struct {
10588586Sjoe	int	num[2];
10688586Sjoe	int	bold;
10788583Sjoe} colors[C_NUMCOLORS];
10861268Sjoe#endif
10961178Sjoe
110291959Sbaptstatic size_t padding_for_month[12];
111291959Sbaptstatic size_t month_max_size = 0;
112291959Sbapt
1131556Srgrimesvoid
114114583Smarkmprintscol(const DISPLAY *dp)
1151556Srgrimes{
11688594Sjoe	FTSENT *p;
1171556Srgrimes
118284198Smarcel	xo_open_list("entry");
1191556Srgrimes	for (p = dp->list; p; p = p->fts_link) {
1201556Srgrimes		if (IS_NOPRINT(p))
1211556Srgrimes			continue;
122284198Smarcel		xo_open_instance("entry");
1231556Srgrimes		(void)printaname(p, dp->s_inode, dp->s_block);
124284198Smarcel		xo_close_instance("entry");
125284198Smarcel		xo_emit("\n");
1261556Srgrimes	}
127284198Smarcel	xo_close_list("entry");
1281556Srgrimes}
1291556Srgrimes
13062597Sassar/*
13162597Sassar * print name in current style
13262597Sassar */
133105390Stjrint
134284198Smarcelprintname(const char *field, const char *name)
13562597Sassar{
136284198Smarcel	char fmt[BUFSIZ];
137284198Smarcel	char *s = getname(name);
138284198Smarcel	int rc;
139284198Smarcel
140284198Smarcel	snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field);
141284198Smarcel	rc = xo_emit(fmt, s);
142284198Smarcel	free(s);
143284198Smarcel	return rc;
144284198Smarcel}
145284198Smarcel
146291959Sbaptstatic const char *
147291959Sbaptget_abmon(int mon)
148291959Sbapt{
149291959Sbapt
150291959Sbapt	switch (mon) {
151291959Sbapt	case 0: return (nl_langinfo(ABMON_1));
152291959Sbapt	case 1: return (nl_langinfo(ABMON_2));
153291959Sbapt	case 2: return (nl_langinfo(ABMON_3));
154291959Sbapt	case 3: return (nl_langinfo(ABMON_4));
155291959Sbapt	case 4: return (nl_langinfo(ABMON_5));
156291959Sbapt	case 5: return (nl_langinfo(ABMON_6));
157291959Sbapt	case 6: return (nl_langinfo(ABMON_7));
158291959Sbapt	case 7: return (nl_langinfo(ABMON_8));
159291959Sbapt	case 8: return (nl_langinfo(ABMON_9));
160291959Sbapt	case 9: return (nl_langinfo(ABMON_10));
161291959Sbapt	case 10: return (nl_langinfo(ABMON_11));
162291959Sbapt	case 11: return (nl_langinfo(ABMON_12));
163291959Sbapt	}
164291959Sbapt
165291959Sbapt	/* should never happen */
166291959Sbapt	abort();
167291959Sbapt}
168291959Sbapt
169291959Sbaptstatic size_t
170291959Sbaptmbswidth(const char *month)
171291959Sbapt{
172291959Sbapt	wchar_t wc;
173291959Sbapt	size_t width, donelen, clen, w;
174291959Sbapt
175291959Sbapt	width = donelen = 0;
176291959Sbapt	while ((clen = mbrtowc(&wc, month + donelen, MB_LEN_MAX, NULL)) != 0) {
177291959Sbapt		if (clen == (size_t)-1 || clen == (size_t)-2)
178291959Sbapt			return (-1);
179291959Sbapt		donelen += clen;
180291959Sbapt		if ((w = wcwidth(wc)) == (size_t)-1)
181291959Sbapt			return (-1);
182291959Sbapt		width += w;
183291959Sbapt	}
184291959Sbapt
185291959Sbapt	return (width);
186291959Sbapt}
187291959Sbapt
188291959Sbaptstatic void
189291959Sbaptcompute_abbreviated_month_size(void)
190291959Sbapt{
191291959Sbapt	int i;
192291959Sbapt	size_t width;
193291959Sbapt	size_t months_width[12];
194291959Sbapt
195291959Sbapt	for (i = 0; i < 12; i++) {
196291959Sbapt		width = mbswidth(get_abmon(i));
197291959Sbapt		if (width == (size_t)-1) {
198291959Sbapt			month_max_size = -1;
199291959Sbapt			return;
200291959Sbapt		}
201291959Sbapt		months_width[i] = width;
202291959Sbapt		if (width > month_max_size)
203291959Sbapt			month_max_size = width;
204291959Sbapt	}
205291959Sbapt
206291959Sbapt	for (i = 0; i < 12; i++)
207291959Sbapt		padding_for_month[i] = month_max_size - months_width[i];
208291959Sbapt}
209291959Sbapt
210284198Smarcel/*
211284198Smarcel * print name in current style
212284198Smarcel */
213284198Smarcelchar *
214284198Smarcelgetname(const char *name)
215284198Smarcel{
21662597Sassar	if (f_octal || f_octal_escape)
217284198Smarcel		return get_octal(name);
21862597Sassar	else if (f_nonprint)
219284198Smarcel		return get_printable(name);
22062597Sassar	else
221284198Smarcel		return strdup(name);
22262597Sassar}
22362597Sassar
2241556Srgrimesvoid
225114583Smarkmprintlong(const DISPLAY *dp)
2261556Srgrimes{
2271556Srgrimes	struct stat *sp;
22888594Sjoe	FTSENT *p;
22988594Sjoe	NAMES *np;
23088594Sjoe	char buf[20];
23161292Sache#ifdef COLORLS
23288594Sjoe	int color_printed = 0;
23361292Sache#endif
2341556Srgrimes
235130236Sdas	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
236130236Sdas	    (f_longform || f_size)) {
237284198Smarcel		xo_emit("{L:total} {:total-blocks/%lu}\n",
238284198Smarcel			howmany(dp->btotal, blocksize));
239130236Sdas	}
2401556Srgrimes
241284198Smarcel	xo_open_list("entry");
2421556Srgrimes	for (p = dp->list; p; p = p->fts_link) {
243285734Sallanjude		char *name, *type;
2441556Srgrimes		if (IS_NOPRINT(p))
2451556Srgrimes			continue;
246284198Smarcel		xo_open_instance("entry");
2471556Srgrimes		sp = p->fts_statp;
248284198Smarcel		name = getname(p->fts_name);
249284198Smarcel		if (name)
250284494Skan		    xo_emit("{ke:name/%hs}", name);
2511556Srgrimes		if (f_inode)
252285734Sallanjude			xo_emit("{t:inode/%*ju} ",
253241014Smdf			    dp->s_inode, (uintmax_t)sp->st_ino);
2541556Srgrimes		if (f_size)
255285734Sallanjude			xo_emit("{t:blocks/%*jd} ",
2561556Srgrimes			    dp->s_block, howmany(sp->st_blocks, blocksize));
25790150Smarkm		strmode(sp->st_mode, buf);
258196712Strasz		aclmode(buf, p);
2591556Srgrimes		np = p->fts_pointer;
260284198Smarcel		xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS);
261285734Sallanjude		if (f_numericonly) {
262314129Skib			xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*ju} {td:user/%-*s}{e:user/%ju}  {td:group/%-*s}{e:group/%ju}  ",
263314129Skib				buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)sp->st_nlink,
264285857Sallanjude				dp->s_user, np->user, (uintmax_t)sp->st_uid, dp->s_group, np->group, (uintmax_t)sp->st_gid);
265285734Sallanjude		} else {
266314129Skib			xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*ju} {t:user/%-*s}  {t:group/%-*s}  ",
267314129Skib				buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)sp->st_nlink,
268285734Sallanjude				dp->s_user, np->user, dp->s_group, np->group);
269285734Sallanjude		}
270285734Sallanjude		if (S_ISBLK(sp->st_mode))
271285734Sallanjude			asprintf(&type, "block");
272285734Sallanjude		if (S_ISCHR(sp->st_mode))
273285734Sallanjude			asprintf(&type, "character");
274285734Sallanjude		if (S_ISDIR(sp->st_mode))
275285734Sallanjude			asprintf(&type, "directory");
276285734Sallanjude		if (S_ISFIFO(sp->st_mode))
277285734Sallanjude			asprintf(&type, "fifo");
278285734Sallanjude		if (S_ISLNK(sp->st_mode))
279285734Sallanjude			asprintf(&type, "symlink");
280285734Sallanjude		if (S_ISREG(sp->st_mode))
281285734Sallanjude			asprintf(&type, "regular");
282285734Sallanjude		if (S_ISSOCK(sp->st_mode))
283285734Sallanjude			asprintf(&type, "socket");
284285734Sallanjude		if (S_ISWHT(sp->st_mode))
285285734Sallanjude			asprintf(&type, "whiteout");
286285734Sallanjude		xo_emit("{e:type/%s}", type);
287285734Sallanjude		free(type);
2881556Srgrimes		if (f_flags)
289284198Smarcel			xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags);
290105832Srwatson		if (f_label)
291285734Sallanjude			xo_emit("{t:label/%-*s} ", dp->s_label, np->label);
2921556Srgrimes		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
293202945Sjh			printdev(dp->s_size, sp->st_rdev);
2941556Srgrimes		else
295284198Smarcel			printsize("size", dp->s_size, sp->st_size);
2961556Srgrimes		if (f_accesstime)
297284198Smarcel			printtime("access-time", sp->st_atime);
298157098Sjhb		else if (f_birthtime)
299284198Smarcel			printtime("birth-time", sp->st_birthtime);
3001556Srgrimes		else if (f_statustime)
301284198Smarcel			printtime("change-time", sp->st_ctime);
3021556Srgrimes		else
303284198Smarcel			printtime("modify-time", sp->st_mtime);
30461268Sjoe#ifdef COLORLS
30561178Sjoe		if (f_color)
30661291Sache			color_printed = colortype(sp->st_mode);
30761268Sjoe#endif
308284198Smarcel
309284198Smarcel		if (name) {
310284494Skan		    xo_emit("{dk:name/%hs}", name);
311284198Smarcel		    free(name);
312284198Smarcel		}
313284198Smarcel
31461268Sjoe#ifdef COLORLS
31561291Sache		if (f_color && color_printed)
31661321Sache			endcolor(0);
31761268Sjoe#endif
3181556Srgrimes		if (f_type)
3191556Srgrimes			(void)printtype(sp->st_mode);
3201556Srgrimes		if (S_ISLNK(sp->st_mode))
3211556Srgrimes			printlink(p);
322284198Smarcel		xo_close_instance("entry");
323284198Smarcel		xo_emit("\n");
3241556Srgrimes	}
325284198Smarcel	xo_close_list("entry");
3261556Srgrimes}
3271556Srgrimes
3281556Srgrimesvoid
329114583Smarkmprintstream(const DISPLAY *dp)
33096892Stjr{
33196892Stjr	FTSENT *p;
33296892Stjr	int chcnt;
33396892Stjr
334285734Sallanjude	xo_open_list("entry");
33596892Stjr	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
33696892Stjr		if (p->fts_number == NO_PRINT)
33796892Stjr			continue;
338128823Stjr		/* XXX strlen does not take octal escapes into account. */
33996892Stjr		if (strlen(p->fts_name) + chcnt +
34096892Stjr		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
341284198Smarcel			xo_emit("\n");
34296892Stjr			chcnt = 0;
34396892Stjr		}
344285734Sallanjude		xo_open_instance("file");
34596892Stjr		chcnt += printaname(p, dp->s_inode, dp->s_block);
346285734Sallanjude		xo_close_instance("file");
34796892Stjr		if (p->fts_link) {
348284198Smarcel			xo_emit(", ");
34996892Stjr			chcnt += 2;
35096892Stjr		}
35196892Stjr	}
352285734Sallanjude	xo_close_list("entry");
35396892Stjr	if (chcnt)
354284198Smarcel		xo_emit("\n");
35596892Stjr}
356177907Sgrog
35796892Stjrvoid
358114583Smarkmprintcol(const DISPLAY *dp)
3591556Srgrimes{
3601556Srgrimes	static FTSENT **array;
3611556Srgrimes	static int lastentries = -1;
36288594Sjoe	FTSENT *p;
363121124Stjr	FTSENT **narray;
36488594Sjoe	int base;
36588594Sjoe	int chcnt;
36688594Sjoe	int cnt;
36788594Sjoe	int col;
36888594Sjoe	int colwidth;
36988594Sjoe	int endcol;
37088594Sjoe	int num;
37188594Sjoe	int numcols;
37288594Sjoe	int numrows;
37388594Sjoe	int row;
37488594Sjoe	int tabwidth;
3751556Srgrimes
37637932Shoek	if (f_notabs)
37737932Shoek		tabwidth = 1;
37837932Shoek	else
37937932Shoek		tabwidth = 8;
38037932Shoek
3811556Srgrimes	/*
3821556Srgrimes	 * Have to do random access in the linked list -- build a table
3831556Srgrimes	 * of pointers.
3841556Srgrimes	 */
3851556Srgrimes	if (dp->entries > lastentries) {
386121124Stjr		if ((narray =
3871556Srgrimes		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
3881556Srgrimes			printscol(dp);
389121124Stjr			return;
3901556Srgrimes		}
391121124Stjr		lastentries = dp->entries;
392121124Stjr		array = narray;
3931556Srgrimes	}
3941556Srgrimes	for (p = dp->list, num = 0; p; p = p->fts_link)
3951556Srgrimes		if (p->fts_number != NO_PRINT)
3961556Srgrimes			array[num++] = p;
3971556Srgrimes
3981556Srgrimes	colwidth = dp->maxlen;
3991556Srgrimes	if (f_inode)
4001556Srgrimes		colwidth += dp->s_inode + 1;
4011556Srgrimes	if (f_size)
4021556Srgrimes		colwidth += dp->s_block + 1;
4031556Srgrimes	if (f_type)
4041556Srgrimes		colwidth += 1;
4051556Srgrimes
40637932Shoek	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
4071556Srgrimes	if (termwidth < 2 * colwidth) {
4081556Srgrimes		printscol(dp);
4091556Srgrimes		return;
4101556Srgrimes	}
4111556Srgrimes	numcols = termwidth / colwidth;
4121556Srgrimes	numrows = num / numcols;
4131556Srgrimes	if (num % numcols)
4141556Srgrimes		++numrows;
4151556Srgrimes
416130236Sdas	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
417130236Sdas	    (f_longform || f_size)) {
418284198Smarcel		xo_emit("{L:total} {:total-blocks/%lu}\n",
419284198Smarcel			howmany(dp->btotal, blocksize));
420130236Sdas	}
42196892Stjr
422284198Smarcel	xo_open_list("entry");
423102577Skeramida	base = 0;
4241556Srgrimes	for (row = 0; row < numrows; ++row) {
4251556Srgrimes		endcol = colwidth;
42696892Stjr		if (!f_sortacross)
42796892Stjr			base = row;
42896892Stjr		for (col = 0, chcnt = 0; col < numcols; ++col) {
429284198Smarcel			xo_open_instance("entry");
4301556Srgrimes			chcnt += printaname(array[base], dp->s_inode,
4311556Srgrimes			    dp->s_block);
432284198Smarcel			xo_close_instance("entry");
43396892Stjr			if (f_sortacross)
43496892Stjr				base++;
43596892Stjr			else
43696892Stjr				base += numrows;
43796892Stjr			if (base >= num)
4381556Srgrimes				break;
43937932Shoek			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
44088595Sjoe			    <= endcol) {
44196892Stjr				if (f_sortacross && col + 1 >= numcols)
44296892Stjr					break;
443284198Smarcel				xo_emit(f_notabs ? " " : "\t");
4441556Srgrimes				chcnt = cnt;
4451556Srgrimes			}
4461556Srgrimes			endcol += colwidth;
4471556Srgrimes		}
448284198Smarcel		xo_emit("\n");
4491556Srgrimes	}
450284198Smarcel	xo_close_list("entry");
4511556Srgrimes}
4521556Srgrimes
4531556Srgrimes/*
4541556Srgrimes * print [inode] [size] name
4551556Srgrimes * return # of characters printed, no trailing characters.
4561556Srgrimes */
4571556Srgrimesstatic int
458114583Smarkmprintaname(const FTSENT *p, u_long inodefield, u_long sizefield)
4591556Srgrimes{
4601556Srgrimes	struct stat *sp;
46188594Sjoe	int chcnt;
46261292Sache#ifdef COLORLS
46388594Sjoe	int color_printed = 0;
46461292Sache#endif
4651556Srgrimes
4661556Srgrimes	sp = p->fts_statp;
4671556Srgrimes	chcnt = 0;
4681556Srgrimes	if (f_inode)
469285734Sallanjude		chcnt += xo_emit("{t:inode/%*ju} ",
470241014Smdf		    (int)inodefield, (uintmax_t)sp->st_ino);
4711556Srgrimes	if (f_size)
472285734Sallanjude		chcnt += xo_emit("{t:size/%*jd} ",
4731556Srgrimes		    (int)sizefield, howmany(sp->st_blocks, blocksize));
47461268Sjoe#ifdef COLORLS
47561178Sjoe	if (f_color)
47661291Sache		color_printed = colortype(sp->st_mode);
47761268Sjoe#endif
478284198Smarcel	chcnt += printname("name", p->fts_name);
47961268Sjoe#ifdef COLORLS
48061291Sache	if (f_color && color_printed)
48161321Sache		endcolor(0);
48261268Sjoe#endif
4831556Srgrimes	if (f_type)
4841556Srgrimes		chcnt += printtype(sp->st_mode);
4851556Srgrimes	return (chcnt);
4861556Srgrimes}
4871556Srgrimes
488202945Sjh/*
489202945Sjh * Print device special file major and minor numbers.
490202945Sjh */
4911556Srgrimesstatic void
492202945Sjhprintdev(size_t width, dev_t dev)
493202945Sjh{
494284198Smarcel	xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev);
495202945Sjh}
496202945Sjh
497291959Sbaptstatic size_t
498291959Sbaptls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm)
499291959Sbapt{
500291959Sbapt	char *posb, nfmt[BUFSIZ];
501291959Sbapt	const char *format = fmt;
502291959Sbapt	size_t ret;
503291959Sbapt
504291959Sbapt	if ((posb = strstr(fmt, "%b")) != NULL) {
505291959Sbapt		if (month_max_size == 0) {
506291959Sbapt			compute_abbreviated_month_size();
507291959Sbapt		}
508291959Sbapt		if (month_max_size > 0) {
509291959Sbapt			snprintf(nfmt, sizeof(nfmt),  "%.*s%s%*s%s",
510291959Sbapt			    (int)(posb - fmt), fmt,
511291959Sbapt			    get_abmon(tm->tm_mon),
512291959Sbapt			    (int)padding_for_month[tm->tm_mon],
513291959Sbapt			    "",
514291959Sbapt			    posb + 2);
515291959Sbapt			format = nfmt;
516291959Sbapt		}
517291959Sbapt	}
518291959Sbapt	ret = strftime(str, len, format, tm);
519291959Sbapt	return (ret);
520291959Sbapt}
521291959Sbapt
522202945Sjhstatic void
523284198Smarcelprinttime(const char *field, time_t ftime)
5241556Srgrimes{
52588594Sjoe	char longstring[80];
526284198Smarcel	char fmt[BUFSIZ];
527114583Smarkm	static time_t now = 0;
52888594Sjoe	const char *format;
52988594Sjoe	static int d_first = -1;
5301556Srgrimes
53174566Sache	if (d_first < 0)
53274566Sache		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
53321545Smpp	if (now == 0)
53421545Smpp		now = time(NULL);
53521545Smpp
5369987Swollman#define	SIXMONTHS	((365 / 2) * 86400)
537177907Sgrog	if (f_timeformat)  /* user specified format */
538177907Sgrog		format = f_timeformat;
539177907Sgrog	else if (f_sectime)
54061920Sjoe		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
541177907Sgrog		format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
54221545Smpp	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
54361920Sjoe		/* mmm dd hh:mm || dd mmm hh:mm */
544177907Sgrog		format = d_first ? "%e %b %R" : "%b %e %R";
54561814Sjoe	else
54661920Sjoe		/* mmm dd  yyyy || dd mmm  yyyy */
547242840Speter		format = d_first ? "%e %b  %Y" : "%b %e  %Y";
548291959Sbapt	ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime));
549284198Smarcel
550285734Sallanjude	snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field);
551284198Smarcel	xo_attr("value", "%ld", (long) ftime);
552284198Smarcel	xo_emit(fmt, longstring);
553285803Sallanjude	snprintf(fmt, sizeof(fmt), "{en:%s/%%ld}", field);
554285734Sallanjude	xo_emit(fmt, (long) ftime);
5551556Srgrimes}
5561556Srgrimes
5571556Srgrimesstatic int
55890110Simpprinttype(u_int mode)
5591556Srgrimes{
56096892Stjr
56196892Stjr	if (f_slash) {
56296892Stjr		if ((mode & S_IFMT) == S_IFDIR) {
563284198Smarcel			xo_emit("{D:\\/}{e:type/directory}");
56496892Stjr			return (1);
56596892Stjr		}
56696892Stjr		return (0);
56796892Stjr	}
56896892Stjr
5691556Srgrimes	switch (mode & S_IFMT) {
5701556Srgrimes	case S_IFDIR:
571284198Smarcel		xo_emit("{D:/\\/}{e:type/directory}");
5721556Srgrimes		return (1);
5731556Srgrimes	case S_IFIFO:
574284198Smarcel		xo_emit("{D:|}{e:type/fifo}");
5751556Srgrimes		return (1);
5761556Srgrimes	case S_IFLNK:
577284198Smarcel		xo_emit("{D:@}{e:type/link}");
5781556Srgrimes		return (1);
5791556Srgrimes	case S_IFSOCK:
580284198Smarcel		xo_emit("{D:=}{e:type/socket}");
5811556Srgrimes		return (1);
58220417Ssteve	case S_IFWHT:
583285857Sallanjude		xo_emit("{D:%%}{e:type/whiteout}");
58420417Ssteve		return (1);
58590150Smarkm	default:
58696681Sbillf		break;
5871556Srgrimes	}
5881556Srgrimes	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
589284198Smarcel		xo_emit("{D:*}{e:executable/}");
5901556Srgrimes		return (1);
5911556Srgrimes	}
5921556Srgrimes	return (0);
5931556Srgrimes}
5941556Srgrimes
59561268Sjoe#ifdef COLORLS
59661323Sachestatic int
59790110Simpputch(int c)
59861291Sache{
599284198Smarcel	xo_emit("{D:/%c}", c);
60061321Sache	return 0;
60161291Sache}
60261291Sache
60361323Sachestatic int
60490110Simpwritech(int c)
60561321Sache{
606114583Smarkm	char tmp = (char)c;
60761291Sache
60888602Sjoe	(void)write(STDOUT_FILENO, &tmp, 1);
60961321Sache	return 0;
61061321Sache}
61161321Sache
61261323Sachestatic void
61390110Simpprintcolor(Colors c)
61461178Sjoe{
61588594Sjoe	char *ansiseq;
61661268Sjoe
61788583Sjoe	if (colors[c].bold)
61888583Sjoe		tputs(enter_bold, 1, putch);
61988583Sjoe
62088583Sjoe	if (colors[c].num[0] != -1) {
62188583Sjoe		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
62261321Sache		if (ansiseq)
62361291Sache			tputs(ansiseq, 1, putch);
62461178Sjoe	}
62588583Sjoe	if (colors[c].num[1] != -1) {
62688583Sjoe		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
62761321Sache		if (ansiseq)
62861291Sache			tputs(ansiseq, 1, putch);
62961268Sjoe	}
63061178Sjoe}
63161178Sjoe
63261321Sachestatic void
63390110Simpendcolor(int sig)
63461268Sjoe{
63561321Sache	tputs(ansi_coloff, 1, sig ? writech : putch);
63688583Sjoe	tputs(attrs_off, 1, sig ? writech : putch);
63761268Sjoe}
63861268Sjoe
63961321Sachestatic int
64090110Simpcolortype(mode_t mode)
64161178Sjoe{
64288602Sjoe	switch (mode & S_IFMT) {
64388595Sjoe	case S_IFDIR:
64461178Sjoe		if (mode & S_IWOTH)
64588595Sjoe			if (mode & S_ISTXT)
64688595Sjoe				printcolor(C_WSDIR);
64788595Sjoe			else
64888595Sjoe				printcolor(C_WDIR);
64961178Sjoe		else
65088595Sjoe			printcolor(C_DIR);
65188602Sjoe		return (1);
65288595Sjoe	case S_IFLNK:
65361178Sjoe		printcolor(C_LNK);
65488602Sjoe		return (1);
65588595Sjoe	case S_IFSOCK:
65661178Sjoe		printcolor(C_SOCK);
65788602Sjoe		return (1);
65888595Sjoe	case S_IFIFO:
65961178Sjoe		printcolor(C_FIFO);
66088602Sjoe		return (1);
66188595Sjoe	case S_IFBLK:
66261178Sjoe		printcolor(C_BLK);
66388602Sjoe		return (1);
66488595Sjoe	case S_IFCHR:
66561178Sjoe		printcolor(C_CHR);
66688602Sjoe		return (1);
667114583Smarkm	default:;
66861178Sjoe	}
66961178Sjoe	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
67061178Sjoe		if (mode & S_ISUID)
67188595Sjoe			printcolor(C_SUID);
67261178Sjoe		else if (mode & S_ISGID)
67388595Sjoe			printcolor(C_SGID);
67461178Sjoe		else
67588595Sjoe			printcolor(C_EXEC);
67688602Sjoe		return (1);
67761178Sjoe	}
67888602Sjoe	return (0);
67961178Sjoe}
68061178Sjoe
68161178Sjoevoid
68290110Simpparsecolors(const char *cs)
68361178Sjoe{
68488594Sjoe	int i;
68588594Sjoe	int j;
686105780Smarkm	size_t len;
68788594Sjoe	char c[2];
68888594Sjoe	short legacy_warn = 0;
68961321Sache
69088586Sjoe	if (cs == NULL)
69188602Sjoe		cs = "";	/* LSCOLORS not set */
69261178Sjoe	len = strlen(cs);
693114583Smarkm	for (i = 0; i < (int)C_NUMCOLORS; i++) {
69488583Sjoe		colors[i].bold = 0;
69588583Sjoe
696114583Smarkm		if (len <= 2 * (size_t)i) {
69788586Sjoe			c[0] = defcolors[2 * i];
69888586Sjoe			c[1] = defcolors[2 * i + 1];
69988602Sjoe		} else {
70088586Sjoe			c[0] = cs[2 * i];
70188586Sjoe			c[1] = cs[2 * i + 1];
70261178Sjoe		}
70388602Sjoe		for (j = 0; j < 2; j++) {
70488583Sjoe			/* Legacy colours used 0-7 */
70588583Sjoe			if (c[j] >= '0' && c[j] <= '7') {
70688583Sjoe				colors[i].num[j] = c[j] - '0';
70788583Sjoe				if (!legacy_warn) {
708284198Smarcel					xo_warnx("LSCOLORS should use "
70988588Sjoe					    "characters a-h instead of 0-9 ("
710106479Stjr					    "see the manual page)");
71188583Sjoe				}
71288583Sjoe				legacy_warn = 1;
71388583Sjoe			} else if (c[j] >= 'a' && c[j] <= 'h')
71488583Sjoe				colors[i].num[j] = c[j] - 'a';
71588583Sjoe			else if (c[j] >= 'A' && c[j] <= 'H') {
71688583Sjoe				colors[i].num[j] = c[j] - 'A';
71788583Sjoe				colors[i].bold = 1;
718114583Smarkm			} else if (tolower((unsigned char)c[j]) == 'x')
71988583Sjoe				colors[i].num[j] = -1;
72088583Sjoe			else {
721284198Smarcel				xo_warnx("invalid character '%c' in LSCOLORS"
722106479Stjr				    " env var", c[j]);
72388584Sjoe				colors[i].num[j] = -1;
72461178Sjoe			}
72561178Sjoe		}
72661178Sjoe	}
72761178Sjoe}
72861291Sache
72961323Sachevoid
73090110Simpcolorquit(int sig)
73161291Sache{
73261321Sache	endcolor(sig);
73361294Sache
73488602Sjoe	(void)signal(sig, SIG_DFL);
73588602Sjoe	(void)kill(getpid(), sig);
73661291Sache}
73788602Sjoe
73888602Sjoe#endif /* COLORLS */
73988602Sjoe
7401556Srgrimesstatic void
741105780Smarkmprintlink(const FTSENT *p)
7421556Srgrimes{
74388594Sjoe	int lnklen;
74488594Sjoe	char name[MAXPATHLEN + 1];
74588594Sjoe	char path[MAXPATHLEN + 1];
7461556Srgrimes
7471556Srgrimes	if (p->fts_level == FTS_ROOTLEVEL)
7481556Srgrimes		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
7498855Srgrimes	else
7501556Srgrimes		(void)snprintf(name, sizeof(name),
7511556Srgrimes		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
7521556Srgrimes	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
753284198Smarcel		xo_error("\nls: %s: %s\n", name, strerror(errno));
7541556Srgrimes		return;
7551556Srgrimes	}
7561556Srgrimes	path[lnklen] = '\0';
757284198Smarcel	xo_emit(" -> ");
758284198Smarcel	(void)printname("target", path);
7591556Srgrimes}
76088591Sjoe
76188591Sjoestatic void
762284198Smarcelprintsize(const char *field, size_t width, off_t bytes)
76388591Sjoe{
764284198Smarcel	char fmt[BUFSIZ];
765284198Smarcel
76688591Sjoe	if (f_humanval) {
767202945Sjh		/*
768202945Sjh		 * Reserve one space before the size and allocate room for
769202945Sjh		 * the trailing '\0'.
770202945Sjh		 */
771202945Sjh		char buf[HUMANVALSTR_LEN - 1 + 1];
77288591Sjoe
773129719Spjd		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
774129719Spjd		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
775284198Smarcel		snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width);
776284198Smarcel		xo_attr("value", "%jd", (intmax_t) bytes);
777284198Smarcel		xo_emit(fmt, buf);
778284198Smarcel	} else {		/* with commas */
779242725Sgrog		/* This format assignment needed to work round gcc bug. */
780284198Smarcel		snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ",
781284198Smarcel		     field, (int) width, f_thousands ? "'" : "");
782284198Smarcel		xo_emit(fmt, (intmax_t) bytes);
783284198Smarcel	}
78488591Sjoe}
78588591Sjoe
786196712Strasz/*
787196712Strasz * Add a + after the standard rwxrwxrwx mode if the file has an
788196712Strasz * ACL. strmode() reserves space at the end of the string.
789196712Strasz */
790106371Stjrstatic void
791196712Straszaclmode(char *buf, const FTSENT *p)
792106371Stjr{
793106371Stjr	char name[MAXPATHLEN + 1];
794196712Strasz	int ret, trivial;
795196712Strasz	static dev_t previous_dev = NODEV;
796196712Strasz	static int supports_acls = -1;
797196712Strasz	static int type = ACL_TYPE_ACCESS;
798106371Stjr	acl_t facl;
799106371Stjr
800106371Stjr	/*
801196712Strasz	 * XXX: ACLs are not supported on whiteouts and device files
802196712Strasz	 * residing on UFS.
803106371Stjr	 */
804196712Strasz	if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
805196712Strasz	    S_ISWHT(p->fts_statp->st_mode))
806108066Stjr		return;
807196712Strasz
808196773Strasz	if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
809196773Strasz		return;
810196773Strasz
811196773Strasz	if (p->fts_level == FTS_ROOTLEVEL)
812196773Strasz		snprintf(name, sizeof(name), "%s", p->fts_name);
813196773Strasz	else
814196773Strasz		snprintf(name, sizeof(name), "%s/%s",
815196773Strasz		    p->fts_parent->fts_accpath, p->fts_name);
816196773Strasz
817196712Strasz	if (previous_dev != p->fts_statp->st_dev) {
818196712Strasz		previous_dev = p->fts_statp->st_dev;
819196712Strasz		supports_acls = 0;
820196712Strasz
821196712Strasz		ret = lpathconf(name, _PC_ACL_NFS4);
822196712Strasz		if (ret > 0) {
823196712Strasz			type = ACL_TYPE_NFS4;
824196712Strasz			supports_acls = 1;
825196712Strasz		} else if (ret < 0 && errno != EINVAL) {
826284198Smarcel			xo_warn("%s", name);
827196712Strasz			return;
828196712Strasz		}
829196712Strasz		if (supports_acls == 0) {
830196712Strasz			ret = lpathconf(name, _PC_ACL_EXTENDED);
831196712Strasz			if (ret > 0) {
832196712Strasz				type = ACL_TYPE_ACCESS;
833196712Strasz				supports_acls = 1;
834196712Strasz			} else if (ret < 0 && errno != EINVAL) {
835284198Smarcel				xo_warn("%s", name);
836196712Strasz				return;
837196712Strasz			}
838196712Strasz		}
839108066Stjr	}
840196712Strasz	if (supports_acls == 0)
841106371Stjr		return;
842196712Strasz	facl = acl_get_link_np(name, type);
843196712Strasz	if (facl == NULL) {
844284198Smarcel		xo_warn("%s", name);
845196712Strasz		return;
846106371Stjr	}
847196712Strasz	if (acl_is_trivial_np(facl, &trivial)) {
848106371Stjr		acl_free(facl);
849284198Smarcel		xo_warn("%s", name);
850196712Strasz		return;
851196712Strasz	}
852196712Strasz	if (!trivial)
853196712Strasz		buf[10] = '+';
854196712Strasz	acl_free(facl);
855106371Stjr}
856