print.c revision 88587
1219820Sjeff/*
2219820Sjeff * Copyright (c) 1989, 1993, 1994
3219820Sjeff *	The Regents of the University of California.  All rights reserved.
4219820Sjeff *
5271127Shselasky * This code is derived from software contributed to Berkeley by
6219820Sjeff * Michael Fischbein.
7219820Sjeff *
8219820Sjeff * Redistribution and use in source and binary forms, with or without
9219820Sjeff * modification, are permitted provided that the following conditions
10219820Sjeff * are met:
11219820Sjeff * 1. Redistributions of source code must retain the above copyright
12219820Sjeff *    notice, this list of conditions and the following disclaimer.
13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
14219820Sjeff *    notice, this list of conditions and the following disclaimer in the
15219820Sjeff *    documentation and/or other materials provided with the distribution.
16219820Sjeff * 3. All advertising materials mentioning features or use of this software
17219820Sjeff *    must display the following acknowledgement:
18219820Sjeff *	This product includes software developed by the University of
19219820Sjeff *	California, Berkeley and its contributors.
20219820Sjeff * 4. Neither the name of the University nor the names of its contributors
21219820Sjeff *    may be used to endorse or promote products derived from this software
22219820Sjeff *    without specific prior written permission.
23219820Sjeff *
24219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34219820Sjeff * SUCH DAMAGE.
35219820Sjeff */
36219820Sjeff
37219820Sjeff#ifndef lint
38219820Sjeff#if 0
39219820Sjeffstatic char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
40219820Sjeff#else
41219820Sjeffstatic const char rcsid[] =
42219820Sjeff  "$FreeBSD: head/bin/ls/print.c 88587 2001-12-28 19:26:06Z joe $";
43219820Sjeff#endif
44219820Sjeff#endif /* not lint */
45219820Sjeff
46219820Sjeff#include <sys/param.h>
47219820Sjeff#include <sys/stat.h>
48219820Sjeff
49219820Sjeff#include <err.h>
50219820Sjeff#include <errno.h>
51219820Sjeff#include <fts.h>
52219820Sjeff#include <grp.h>
53219820Sjeff#include <langinfo.h>
54219820Sjeff#include <pwd.h>
55219820Sjeff#include <stdio.h>
56219820Sjeff#include <stdlib.h>
57219820Sjeff#include <string.h>
58219820Sjeff#include <time.h>
59219820Sjeff#include <unistd.h>
60219820Sjeff#ifdef COLORLS
61219820Sjeff#include <ctype.h>
62219820Sjeff#include <termcap.h>
63219820Sjeff#include <signal.h>
64219820Sjeff#endif
65219820Sjeff
66219820Sjeff#include "ls.h"
67219820Sjeff#include "extern.h"
68219820Sjeff
69219820Sjeffstatic int	printaname __P((FTSENT *, u_long, u_long));
70219820Sjeffstatic void	printlink __P((FTSENT *));
71219820Sjeffstatic void	printtime __P((time_t));
72219820Sjeffstatic int	printtype __P((u_int));
73219820Sjeff#ifdef COLORLS
74219820Sjeffstatic void     endcolor __P((int));
75219820Sjeffstatic int      colortype __P((mode_t));
76219820Sjeff#endif
77219820Sjeff
78219820Sjeff#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
79219820Sjeff
80219820Sjeff#ifdef COLORLS
81219820Sjeff/* Most of these are taken from <sys/stat.h> */
82219820Sjefftypedef enum Colors {
83219820Sjeff	C_DIR,		/* directory */
84219820Sjeff	C_LNK,		/* symbolic link */
85219820Sjeff	C_SOCK,		/* socket */
86219820Sjeff	C_FIFO,		/* pipe */
87219820Sjeff	C_EXEC,		/* executable */
88219820Sjeff	C_BLK,		/* block special */
89219820Sjeff	C_CHR,		/* character special */
90219820Sjeff	C_SUID,		/* setuid executable */
91219820Sjeff	C_SGID,		/* setgid executable */
92219820Sjeff	C_WSDIR,	/* directory writeble to others, with sticky bit */
93219820Sjeff	C_WDIR,		/* directory writeble to others, without sticky bit */
94219820Sjeff	C_NUMCOLORS	/* just a place-holder */
95219820Sjeff} Colors;
96219820Sjeff
97219820Sjeffconst char *defcolors = "exfxcxdxbxegedabagacad";
98219820Sjeff
99219820Sjeff/* colors for file types */
100271127Shselaskystatic struct {
101271127Shselasky	int	num[2];
102271127Shselasky	int	bold;
103271127Shselasky} colors[C_NUMCOLORS];
104271127Shselasky
105271127Shselasky#endif
106271127Shselasky
107271127Shselaskyvoid
108271127Shselaskyprintscol(dp)
109271127Shselasky	DISPLAY	*dp;
110271127Shselasky{
111271127Shselasky	FTSENT	*p;
112271127Shselasky
113271127Shselasky	for (p = dp->list; p; p = p->fts_link) {
114271127Shselasky		if (IS_NOPRINT(p))
115271127Shselasky			continue;
116271127Shselasky		(void)printaname(p, dp->s_inode, dp->s_block);
117271127Shselasky		(void)putchar('\n');
118219820Sjeff	}
119219820Sjeff}
120219820Sjeff
121219820Sjeff/*
122219820Sjeff * print name in current style
123219820Sjeff */
124219820Sjeffstatic int
125219820Sjeffprintname(name)
126219820Sjeff	const char *name;
127219820Sjeff{
128271127Shselasky	if (f_octal || f_octal_escape)
129271127Shselasky		return prn_octal(name);
130271127Shselasky	else if (f_nonprint)
131219820Sjeff		return prn_printable(name);
132219820Sjeff	else
133219820Sjeff		return printf("%s", name);
134219820Sjeff}
135271127Shselasky
136271127Shselaskyvoid
137271127Shselaskyprintlong(dp)
138271127Shselasky	DISPLAY *dp;
139271127Shselasky{
140271127Shselasky	struct stat *sp;
141271127Shselasky	FTSENT	*p;
142271127Shselasky	NAMES	*np;
143271127Shselasky	char	buf[20];
144219820Sjeff#ifdef COLORLS
145219820Sjeff	int	color_printed = 0;
146219820Sjeff#endif
147219820Sjeff
148219820Sjeff	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
149219820Sjeff		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
150219820Sjeff
151271127Shselasky	for (p = dp->list; p; p = p->fts_link) {
152271127Shselasky		if (IS_NOPRINT(p))
153271127Shselasky			continue;
154219820Sjeff		sp = p->fts_statp;
155219820Sjeff		if (f_inode)
156219820Sjeff			(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
157271127Shselasky		if (f_size)
158271127Shselasky			(void)printf("%*qd ",
159271127Shselasky			    dp->s_block, howmany(sp->st_blocks, blocksize));
160271127Shselasky		(void)strmode(sp->st_mode, buf);
161271127Shselasky		np = p->fts_pointer;
162271127Shselasky		(void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
163271127Shselasky		    sp->st_nlink, dp->s_user, np->user, dp->s_group,
164271127Shselasky		    np->group);
165271127Shselasky		if (f_flags)
166271127Shselasky			(void)printf("%-*s ", dp->s_flags, np->flags);
167271127Shselasky		if (f_lomac)
168219820Sjeff			(void)printf("%-*s ", dp->s_lattr, np->lattr);
169219820Sjeff		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
170219820Sjeff			if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
171219820Sjeff				(void)printf("%3d, 0x%08x ",
172219820Sjeff				    major(sp->st_rdev),
173219820Sjeff				    (u_int)minor(sp->st_rdev));
174219820Sjeff			else
175219820Sjeff				(void)printf("%3d, %3d ",
176219820Sjeff				    major(sp->st_rdev), minor(sp->st_rdev));
177219820Sjeff		else if (dp->bcfile)
178219820Sjeff			(void)printf("%*s%*qd ",
179219820Sjeff			    8 - dp->s_size, "", dp->s_size, sp->st_size);
180219820Sjeff		else
181219820Sjeff			(void)printf("%*qd ", dp->s_size, sp->st_size);
182219820Sjeff		if (f_accesstime)
183219820Sjeff			printtime(sp->st_atime);
184219820Sjeff		else if (f_statustime)
185219820Sjeff			printtime(sp->st_ctime);
186219820Sjeff		else
187219820Sjeff			printtime(sp->st_mtime);
188219820Sjeff#ifdef COLORLS
189219820Sjeff		if (f_color)
190219820Sjeff			color_printed = colortype(sp->st_mode);
191219820Sjeff#endif
192219820Sjeff		(void)printname(p->fts_name);
193219820Sjeff#ifdef COLORLS
194219820Sjeff		if (f_color && color_printed)
195219820Sjeff			endcolor(0);
196219820Sjeff#endif
197219820Sjeff		if (f_type)
198219820Sjeff			(void)printtype(sp->st_mode);
199219820Sjeff		if (S_ISLNK(sp->st_mode))
200219820Sjeff			printlink(p);
201219820Sjeff		(void)putchar('\n');
202219820Sjeff	}
203219820Sjeff}
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