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