print.c revision 88587
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 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
40#else
41static const char rcsid[] =
42  "$FreeBSD: head/bin/ls/print.c 88587 2001-12-28 19:26:06Z joe $";
43#endif
44#endif /* not lint */
45
46#include <sys/param.h>
47#include <sys/stat.h>
48
49#include <err.h>
50#include <errno.h>
51#include <fts.h>
52#include <grp.h>
53#include <langinfo.h>
54#include <pwd.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <time.h>
59#include <unistd.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 __P((FTSENT *, u_long, u_long));
70static void	printlink __P((FTSENT *));
71static void	printtime __P((time_t));
72static int	printtype __P((u_int));
73#ifdef COLORLS
74static void     endcolor __P((int));
75static int      colortype __P((mode_t));
76#endif
77
78#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
79
80#ifdef COLORLS
81/* Most of these are taken from <sys/stat.h> */
82typedef enum Colors {
83	C_DIR,		/* directory */
84	C_LNK,		/* symbolic link */
85	C_SOCK,		/* socket */
86	C_FIFO,		/* pipe */
87	C_EXEC,		/* executable */
88	C_BLK,		/* block special */
89	C_CHR,		/* character special */
90	C_SUID,		/* setuid executable */
91	C_SGID,		/* setgid executable */
92	C_WSDIR,	/* directory writeble to others, with sticky bit */
93	C_WDIR,		/* directory writeble to others, without sticky bit */
94	C_NUMCOLORS	/* just a place-holder */
95} Colors;
96
97const char *defcolors = "exfxcxdxbxegedabagacad";
98
99/* colors for file types */
100static struct {
101	int	num[2];
102	int	bold;
103} colors[C_NUMCOLORS];
104
105#endif
106
107void
108printscol(dp)
109	DISPLAY	*dp;
110{
111	FTSENT	*p;
112
113	for (p = dp->list; p; p = p->fts_link) {
114		if (IS_NOPRINT(p))
115			continue;
116		(void)printaname(p, dp->s_inode, dp->s_block);
117		(void)putchar('\n');
118	}
119}
120
121/*
122 * print name in current style
123 */
124static int
125printname(name)
126	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 printf("%s", name);
134}
135
136void
137printlong(dp)
138	DISPLAY *dp;
139{
140	struct stat *sp;
141	FTSENT	*p;
142	NAMES	*np;
143	char	buf[20];
144#ifdef COLORLS
145	int	color_printed = 0;
146#endif
147
148	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
149		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
150
151	for (p = dp->list; p; p = p->fts_link) {
152		if (IS_NOPRINT(p))
153			continue;
154		sp = p->fts_statp;
155		if (f_inode)
156			(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
157		if (f_size)
158			(void)printf("%*qd ",
159			    dp->s_block, howmany(sp->st_blocks, blocksize));
160		(void)strmode(sp->st_mode, buf);
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_lomac)
168			(void)printf("%-*s ", dp->s_lattr, np->lattr);
169		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
170			if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
171				(void)printf("%3d, 0x%08x ",
172				    major(sp->st_rdev),
173				    (u_int)minor(sp->st_rdev));
174			else
175				(void)printf("%3d, %3d ",
176				    major(sp->st_rdev), minor(sp->st_rdev));
177		else if (dp->bcfile)
178			(void)printf("%*s%*qd ",
179			    8 - dp->s_size, "", dp->s_size, sp->st_size);
180		else
181			(void)printf("%*qd ", dp->s_size, sp->st_size);
182		if (f_accesstime)
183			printtime(sp->st_atime);
184		else if (f_statustime)
185			printtime(sp->st_ctime);
186		else
187			printtime(sp->st_mtime);
188#ifdef COLORLS
189		if (f_color)
190			color_printed = colortype(sp->st_mode);
191#endif
192		(void)printname(p->fts_name);
193#ifdef COLORLS
194		if (f_color && color_printed)
195			endcolor(0);
196#endif
197		if (f_type)
198			(void)printtype(sp->st_mode);
199		if (S_ISLNK(sp->st_mode))
200			printlink(p);
201		(void)putchar('\n');
202	}
203}
204
205void
206printcol(dp)
207	DISPLAY	*dp;
208{
209	extern int termwidth;
210	static FTSENT **array;
211	static int lastentries = -1;
212	FTSENT	*p;
213	int	base;
214	int	chcnt;
215	int	cnt;
216	int	col;
217	int	colwidth;
218	int	endcol;
219	int	num;
220	int	numcols;
221	int	numrows;
222	int	row;
223	int	tabwidth;
224
225	if (f_notabs)
226		tabwidth = 1;
227	else
228		tabwidth = 8;
229
230	/*
231	 * Have to do random access in the linked list -- build a table
232	 * of pointers.
233	 */
234	if (dp->entries > lastentries) {
235		lastentries = dp->entries;
236		if ((array =
237		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
238			warn(NULL);
239			printscol(dp);
240		}
241	}
242	for (p = dp->list, num = 0; p; p = p->fts_link)
243		if (p->fts_number != NO_PRINT)
244			array[num++] = p;
245
246	colwidth = dp->maxlen;
247	if (f_inode)
248		colwidth += dp->s_inode + 1;
249	if (f_size)
250		colwidth += dp->s_block + 1;
251	if (f_type)
252		colwidth += 1;
253
254	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
255	if (termwidth < 2 * colwidth) {
256		printscol(dp);
257		return;
258	}
259
260	numcols = termwidth / colwidth;
261	numrows = num / numcols;
262	if (num % numcols)
263		++numrows;
264
265	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
266		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
267	for (row = 0; row < numrows; ++row) {
268		endcol = colwidth;
269		for (base = row, chcnt = col = 0; col < numcols; ++col) {
270			chcnt += printaname(array[base], dp->s_inode,
271			    dp->s_block);
272			if ((base += numrows) >= num)
273				break;
274			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
275			    <= endcol){
276				(void)putchar(f_notabs ? ' ' : '\t');
277				chcnt = cnt;
278			}
279			endcol += colwidth;
280		}
281		(void)putchar('\n');
282	}
283}
284
285/*
286 * print [inode] [size] name
287 * return # of characters printed, no trailing characters.
288 */
289static int
290printaname(p, inodefield, sizefield)
291	FTSENT	*p;
292	u_long	inodefield;
293	u_long	sizefield;
294{
295	struct stat *sp;
296	int	chcnt;
297#ifdef COLORLS
298	int	color_printed = 0;
299#endif
300
301	sp = p->fts_statp;
302	chcnt = 0;
303	if (f_inode)
304		chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
305	if (f_size)
306		chcnt += printf("%*qd ",
307		    (int)sizefield, howmany(sp->st_blocks, blocksize));
308#ifdef COLORLS
309	if (f_color)
310		color_printed = colortype(sp->st_mode);
311#endif
312	chcnt += printname(p->fts_name);
313#ifdef COLORLS
314	if (f_color && color_printed)
315		endcolor(0);
316#endif
317	if (f_type)
318		chcnt += printtype(sp->st_mode);
319	return (chcnt);
320}
321
322static void
323printtime(ftime)
324	time_t	ftime;
325{
326	char		longstring[80];
327	static time_t	now;
328	const char	*format;
329	static int	d_first = -1;
330
331	if (d_first < 0)
332		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
333	if (now == 0)
334		now = time(NULL);
335
336#define	SIXMONTHS	((365 / 2) * 86400)
337	if (f_sectime)
338		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
339		format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
340	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
341		/* mmm dd hh:mm || dd mmm hh:mm */
342		format = d_first ? "%e %b %R " : "%b %e %R ";
343	else
344		/* mmm dd  yyyy || dd mmm  yyyy */
345		format = d_first ? "%e %b  %Y " : "%b %e  %Y ";
346	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
347	fputs(longstring, stdout);
348}
349
350static int
351printtype(mode)
352	u_int	mode;
353{
354	switch (mode & S_IFMT) {
355	case S_IFDIR:
356		(void)putchar('/');
357		return (1);
358	case S_IFIFO:
359		(void)putchar('|');
360		return (1);
361	case S_IFLNK:
362		(void)putchar('@');
363		return (1);
364	case S_IFSOCK:
365		(void)putchar('=');
366		return (1);
367	case S_IFWHT:
368		(void)putchar('%');
369		return (1);
370	}
371	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
372		(void)putchar('*');
373		return (1);
374	}
375	return (0);
376}
377
378#ifdef COLORLS
379static int
380putch(c)
381	int	c;
382{
383	(void) putchar(c);
384	return 0;
385}
386
387static int
388writech(c)
389	int	c;
390{
391	char	tmp = c;
392
393	(void) write(STDOUT_FILENO, &tmp, 1);
394	return 0;
395}
396
397static void
398printcolor(c)
399	Colors	c;
400{
401	char	*ansiseq;
402
403	if (colors[c].bold)
404		tputs(enter_bold, 1, putch);
405
406	if (colors[c].num[0] != -1) {
407		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
408		if (ansiseq)
409			tputs(ansiseq, 1, putch);
410	}
411
412	if (colors[c].num[1] != -1) {
413		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
414		if (ansiseq)
415			tputs(ansiseq, 1, putch);
416	}
417}
418
419static void
420endcolor(sig)
421	int	sig;
422{
423	tputs(ansi_coloff, 1, sig ? writech : putch);
424	tputs(attrs_off, 1, sig ? writech : putch);
425}
426
427static int
428colortype(mode)
429	mode_t	mode;
430{
431	switch(mode & S_IFMT) {
432	      case S_IFDIR:
433		if (mode & S_IWOTH)
434		    if (mode & S_ISTXT)
435			printcolor(C_WSDIR);
436		    else
437			printcolor(C_WDIR);
438		else
439		    printcolor(C_DIR);
440		return(1);
441	      case S_IFLNK:
442		printcolor(C_LNK);
443		return(1);
444	      case S_IFSOCK:
445		printcolor(C_SOCK);
446		return(1);
447	      case S_IFIFO:
448		printcolor(C_FIFO);
449		return(1);
450	      case S_IFBLK:
451		printcolor(C_BLK);
452		return(1);
453	      case S_IFCHR:
454		printcolor(C_CHR);
455		return(1);
456	}
457	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
458		if (mode & S_ISUID)
459		    printcolor(C_SUID);
460		else if (mode & S_ISGID)
461		    printcolor(C_SGID);
462		else
463		    printcolor(C_EXEC);
464		return(1);
465	}
466	return(0);
467}
468
469void
470parsecolors(cs)
471	const char *cs;
472{
473	int	i;
474	int	j;
475	int	len;
476	char	c[2];
477	short	legacy_warn = 0;
478
479	if (cs == NULL)
480		cs = ""; /* LSCOLORS not set */
481	len = strlen(cs);
482	for (i = 0 ; i < C_NUMCOLORS ; i++) {
483		colors[i].bold = 0;
484
485		if (len <= 2 * i) {
486			c[0] = defcolors[2 * i];
487			c[1] = defcolors[2 * i + 1];
488		}
489		else {
490			c[0] = cs[2 * i];
491			c[1] = cs[2 * i + 1];
492		}
493		for (j = 0 ; j < 2 ; j++) {
494			/* Legacy colours used 0-7 */
495			if (c[j] >= '0' && c[j] <= '7') {
496				colors[i].num[j] = c[j] - '0';
497				if (!legacy_warn) {
498					fprintf(stderr,
499					    "warn: colors are now defined "
500					    "using a-h instead of 0-9. "
501					    "see manual page.\n");
502				}
503				legacy_warn = 1;
504			} else if (c[j] >= 'a' && c[j] <= 'h')
505				colors[i].num[j] = c[j] - 'a';
506			else if (c[j] >= 'A' && c[j] <= 'H') {
507				colors[i].num[j] = c[j] - 'A';
508				colors[i].bold = 1;
509			} else if (tolower((unsigned char)c[j] == 'x'))
510				colors[i].num[j] = -1;
511			else {
512				fprintf(stderr,
513				    "error: invalid character '%c' in LSCOLORS"
514				    " env var\n", c[j]);
515				colors[i].num[j] = -1;
516			}
517		}
518	}
519}
520
521void
522colorquit(sig)
523	int	sig;
524{
525	endcolor(sig);
526
527	(void) signal(sig, SIG_DFL);
528	(void) kill(getpid(), sig);
529}
530#endif /*COLORLS*/
531
532static void
533printlink(p)
534	FTSENT	*p;
535{
536	int	lnklen;
537	char	name[MAXPATHLEN + 1];
538	char	path[MAXPATHLEN + 1];
539
540	if (p->fts_level == FTS_ROOTLEVEL)
541		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
542	else
543		(void)snprintf(name, sizeof(name),
544		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
545	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
546		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
547		return;
548	}
549	path[lnklen] = '\0';
550	(void)printf(" -> ");
551	printname(path);
552}
553