print.c revision 242725
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 242725 2012-11-08 00:24:26Z grog $");
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("%*ju ",
156			    dp->s_inode, (uintmax_t)sp->st_ino);
157		if (f_size)
158			(void)printf("%*jd ",
159			    dp->s_block, howmany(sp->st_blocks, blocksize));
160		strmode(sp->st_mode, buf);
161		aclmode(buf, p);
162		np = p->fts_pointer;
163		(void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
164		    sp->st_nlink, dp->s_user, np->user, dp->s_group,
165		    np->group);
166		if (f_flags)
167			(void)printf("%-*s ", dp->s_flags, np->flags);
168		if (f_label)
169			(void)printf("%-*s ", dp->s_label, np->label);
170		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
171			printdev(dp->s_size, sp->st_rdev);
172		else
173			printsize(dp->s_size, sp->st_size);
174		if (f_accesstime)
175			printtime(sp->st_atime);
176		else if (f_birthtime)
177			printtime(sp->st_birthtime);
178		else if (f_statustime)
179			printtime(sp->st_ctime);
180		else
181			printtime(sp->st_mtime);
182#ifdef COLORLS
183		if (f_color)
184			color_printed = colortype(sp->st_mode);
185#endif
186		(void)printname(p->fts_name);
187#ifdef COLORLS
188		if (f_color && color_printed)
189			endcolor(0);
190#endif
191		if (f_type)
192			(void)printtype(sp->st_mode);
193		if (S_ISLNK(sp->st_mode))
194			printlink(p);
195		(void)putchar('\n');
196	}
197}
198
199void
200printstream(const DISPLAY *dp)
201{
202	FTSENT *p;
203	int chcnt;
204
205	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
206		if (p->fts_number == NO_PRINT)
207			continue;
208		/* XXX strlen does not take octal escapes into account. */
209		if (strlen(p->fts_name) + chcnt +
210		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
211			putchar('\n');
212			chcnt = 0;
213		}
214		chcnt += printaname(p, dp->s_inode, dp->s_block);
215		if (p->fts_link) {
216			printf(", ");
217			chcnt += 2;
218		}
219	}
220	if (chcnt)
221		putchar('\n');
222}
223
224void
225printcol(const DISPLAY *dp)
226{
227	static FTSENT **array;
228	static int lastentries = -1;
229	FTSENT *p;
230	FTSENT **narray;
231	int base;
232	int chcnt;
233	int cnt;
234	int col;
235	int colwidth;
236	int endcol;
237	int num;
238	int numcols;
239	int numrows;
240	int row;
241	int tabwidth;
242
243	if (f_notabs)
244		tabwidth = 1;
245	else
246		tabwidth = 8;
247
248	/*
249	 * Have to do random access in the linked list -- build a table
250	 * of pointers.
251	 */
252	if (dp->entries > lastentries) {
253		if ((narray =
254		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
255			warn(NULL);
256			printscol(dp);
257			return;
258		}
259		lastentries = dp->entries;
260		array = narray;
261	}
262	for (p = dp->list, num = 0; p; p = p->fts_link)
263		if (p->fts_number != NO_PRINT)
264			array[num++] = p;
265
266	colwidth = dp->maxlen;
267	if (f_inode)
268		colwidth += dp->s_inode + 1;
269	if (f_size)
270		colwidth += dp->s_block + 1;
271	if (f_type)
272		colwidth += 1;
273
274	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
275	if (termwidth < 2 * colwidth) {
276		printscol(dp);
277		return;
278	}
279	numcols = termwidth / colwidth;
280	numrows = num / numcols;
281	if (num % numcols)
282		++numrows;
283
284	if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) &&
285	    (f_longform || f_size)) {
286		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
287	}
288
289	base = 0;
290	for (row = 0; row < numrows; ++row) {
291		endcol = colwidth;
292		if (!f_sortacross)
293			base = row;
294		for (col = 0, chcnt = 0; col < numcols; ++col) {
295			chcnt += printaname(array[base], dp->s_inode,
296			    dp->s_block);
297			if (f_sortacross)
298				base++;
299			else
300				base += numrows;
301			if (base >= num)
302				break;
303			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
304			    <= endcol) {
305				if (f_sortacross && col + 1 >= numcols)
306					break;
307				(void)putchar(f_notabs ? ' ' : '\t');
308				chcnt = cnt;
309			}
310			endcol += colwidth;
311		}
312		(void)putchar('\n');
313	}
314}
315
316/*
317 * print [inode] [size] name
318 * return # of characters printed, no trailing characters.
319 */
320static int
321printaname(const FTSENT *p, u_long inodefield, u_long sizefield)
322{
323	struct stat *sp;
324	int chcnt;
325#ifdef COLORLS
326	int color_printed = 0;
327#endif
328
329	sp = p->fts_statp;
330	chcnt = 0;
331	if (f_inode)
332		chcnt += printf("%*ju ",
333		    (int)inodefield, (uintmax_t)sp->st_ino);
334	if (f_size)
335		chcnt += printf("%*jd ",
336		    (int)sizefield, howmany(sp->st_blocks, blocksize));
337#ifdef COLORLS
338	if (f_color)
339		color_printed = colortype(sp->st_mode);
340#endif
341	chcnt += printname(p->fts_name);
342#ifdef COLORLS
343	if (f_color && color_printed)
344		endcolor(0);
345#endif
346	if (f_type)
347		chcnt += printtype(sp->st_mode);
348	return (chcnt);
349}
350
351/*
352 * Print device special file major and minor numbers.
353 */
354static void
355printdev(size_t width, dev_t dev)
356{
357
358	(void)printf("%#*jx ", (u_int)width, (uintmax_t)dev);
359}
360
361static void
362printtime(time_t ftime)
363{
364	char longstring[80];
365	static time_t now = 0;
366	const char *format;
367	static int d_first = -1;
368
369	if (d_first < 0)
370		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
371	if (now == 0)
372		now = time(NULL);
373
374#define	SIXMONTHS	((365 / 2) * 86400)
375	if (f_timeformat)  /* user specified format */
376		format = f_timeformat;
377	else if (f_sectime)
378		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
379		format = d_first ? "%e %b %T %Y" : "%b %e %T %Y";
380	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
381		/* mmm dd hh:mm || dd mmm hh:mm */
382		format = d_first ? "%e %b %R" : "%b %e %R";
383	else
384		/* mmm dd  yyyy || dd mmm  yyyy */
385		format = d_first ? "%e %b  %Y" : "%b %e  %Y";
386	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
387	fputs(longstring, stdout);
388	fputc(' ', stdout);
389}
390
391static int
392printtype(u_int mode)
393{
394
395	if (f_slash) {
396		if ((mode & S_IFMT) == S_IFDIR) {
397			(void)putchar('/');
398			return (1);
399		}
400		return (0);
401	}
402
403	switch (mode & S_IFMT) {
404	case S_IFDIR:
405		(void)putchar('/');
406		return (1);
407	case S_IFIFO:
408		(void)putchar('|');
409		return (1);
410	case S_IFLNK:
411		(void)putchar('@');
412		return (1);
413	case S_IFSOCK:
414		(void)putchar('=');
415		return (1);
416	case S_IFWHT:
417		(void)putchar('%');
418		return (1);
419	default:
420		break;
421	}
422	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
423		(void)putchar('*');
424		return (1);
425	}
426	return (0);
427}
428
429#ifdef COLORLS
430static int
431putch(int c)
432{
433	(void)putchar(c);
434	return 0;
435}
436
437static int
438writech(int c)
439{
440	char tmp = (char)c;
441
442	(void)write(STDOUT_FILENO, &tmp, 1);
443	return 0;
444}
445
446static void
447printcolor(Colors c)
448{
449	char *ansiseq;
450
451	if (colors[c].bold)
452		tputs(enter_bold, 1, putch);
453
454	if (colors[c].num[0] != -1) {
455		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
456		if (ansiseq)
457			tputs(ansiseq, 1, putch);
458	}
459	if (colors[c].num[1] != -1) {
460		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
461		if (ansiseq)
462			tputs(ansiseq, 1, putch);
463	}
464}
465
466static void
467endcolor(int sig)
468{
469	tputs(ansi_coloff, 1, sig ? writech : putch);
470	tputs(attrs_off, 1, sig ? writech : putch);
471}
472
473static int
474colortype(mode_t mode)
475{
476	switch (mode & S_IFMT) {
477	case S_IFDIR:
478		if (mode & S_IWOTH)
479			if (mode & S_ISTXT)
480				printcolor(C_WSDIR);
481			else
482				printcolor(C_WDIR);
483		else
484			printcolor(C_DIR);
485		return (1);
486	case S_IFLNK:
487		printcolor(C_LNK);
488		return (1);
489	case S_IFSOCK:
490		printcolor(C_SOCK);
491		return (1);
492	case S_IFIFO:
493		printcolor(C_FIFO);
494		return (1);
495	case S_IFBLK:
496		printcolor(C_BLK);
497		return (1);
498	case S_IFCHR:
499		printcolor(C_CHR);
500		return (1);
501	default:;
502	}
503	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
504		if (mode & S_ISUID)
505			printcolor(C_SUID);
506		else if (mode & S_ISGID)
507			printcolor(C_SGID);
508		else
509			printcolor(C_EXEC);
510		return (1);
511	}
512	return (0);
513}
514
515void
516parsecolors(const char *cs)
517{
518	int i;
519	int j;
520	size_t len;
521	char c[2];
522	short legacy_warn = 0;
523
524	if (cs == NULL)
525		cs = "";	/* LSCOLORS not set */
526	len = strlen(cs);
527	for (i = 0; i < (int)C_NUMCOLORS; i++) {
528		colors[i].bold = 0;
529
530		if (len <= 2 * (size_t)i) {
531			c[0] = defcolors[2 * i];
532			c[1] = defcolors[2 * i + 1];
533		} else {
534			c[0] = cs[2 * i];
535			c[1] = cs[2 * i + 1];
536		}
537		for (j = 0; j < 2; j++) {
538			/* Legacy colours used 0-7 */
539			if (c[j] >= '0' && c[j] <= '7') {
540				colors[i].num[j] = c[j] - '0';
541				if (!legacy_warn) {
542					warnx("LSCOLORS should use "
543					    "characters a-h instead of 0-9 ("
544					    "see the manual page)");
545				}
546				legacy_warn = 1;
547			} else if (c[j] >= 'a' && c[j] <= 'h')
548				colors[i].num[j] = c[j] - 'a';
549			else if (c[j] >= 'A' && c[j] <= 'H') {
550				colors[i].num[j] = c[j] - 'A';
551				colors[i].bold = 1;
552			} else if (tolower((unsigned char)c[j]) == 'x')
553				colors[i].num[j] = -1;
554			else {
555				warnx("invalid character '%c' in LSCOLORS"
556				    " env var", c[j]);
557				colors[i].num[j] = -1;
558			}
559		}
560	}
561}
562
563void
564colorquit(int sig)
565{
566	endcolor(sig);
567
568	(void)signal(sig, SIG_DFL);
569	(void)kill(getpid(), sig);
570}
571
572#endif /* COLORLS */
573
574static void
575printlink(const FTSENT *p)
576{
577	int lnklen;
578	char name[MAXPATHLEN + 1];
579	char path[MAXPATHLEN + 1];
580
581	if (p->fts_level == FTS_ROOTLEVEL)
582		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
583	else
584		(void)snprintf(name, sizeof(name),
585		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
586	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
587		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
588		return;
589	}
590	path[lnklen] = '\0';
591	(void)printf(" -> ");
592	(void)printname(path);
593}
594
595static void
596printsize(size_t width, off_t bytes)
597{
598
599	if (f_humanval) {
600		/*
601		 * Reserve one space before the size and allocate room for
602		 * the trailing '\0'.
603		 */
604		char buf[HUMANVALSTR_LEN - 1 + 1];
605
606		humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
607		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
608		(void)printf("%*s ", (u_int)width, buf);
609	} else if (f_thousands) {               /* with commas */
610		/* This format assignment needed to work round gcc bug. */
611                const char *format = "%*j'd ";
612		(void)printf(format, (u_int)width, bytes);
613        } else
614		(void)printf("%*jd ", (u_int)width, bytes);
615}
616
617/*
618 * Add a + after the standard rwxrwxrwx mode if the file has an
619 * ACL. strmode() reserves space at the end of the string.
620 */
621static void
622aclmode(char *buf, const FTSENT *p)
623{
624	char name[MAXPATHLEN + 1];
625	int ret, trivial;
626	static dev_t previous_dev = NODEV;
627	static int supports_acls = -1;
628	static int type = ACL_TYPE_ACCESS;
629	acl_t facl;
630
631	/*
632	 * XXX: ACLs are not supported on whiteouts and device files
633	 * residing on UFS.
634	 */
635	if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) ||
636	    S_ISWHT(p->fts_statp->st_mode))
637		return;
638
639	if (previous_dev == p->fts_statp->st_dev && supports_acls == 0)
640		return;
641
642	if (p->fts_level == FTS_ROOTLEVEL)
643		snprintf(name, sizeof(name), "%s", p->fts_name);
644	else
645		snprintf(name, sizeof(name), "%s/%s",
646		    p->fts_parent->fts_accpath, p->fts_name);
647
648	if (previous_dev != p->fts_statp->st_dev) {
649		previous_dev = p->fts_statp->st_dev;
650		supports_acls = 0;
651
652		ret = lpathconf(name, _PC_ACL_NFS4);
653		if (ret > 0) {
654			type = ACL_TYPE_NFS4;
655			supports_acls = 1;
656		} else if (ret < 0 && errno != EINVAL) {
657			warn("%s", name);
658			return;
659		}
660		if (supports_acls == 0) {
661			ret = lpathconf(name, _PC_ACL_EXTENDED);
662			if (ret > 0) {
663				type = ACL_TYPE_ACCESS;
664				supports_acls = 1;
665			} else if (ret < 0 && errno != EINVAL) {
666				warn("%s", name);
667				return;
668			}
669		}
670	}
671	if (supports_acls == 0)
672		return;
673	facl = acl_get_link_np(name, type);
674	if (facl == NULL) {
675		warn("%s", name);
676		return;
677	}
678	if (acl_is_trivial_np(facl, &trivial)) {
679		acl_free(facl);
680		warn("%s", name);
681		return;
682	}
683	if (!trivial)
684		buf[10] = '+';
685	acl_free(facl);
686}
687