print.c revision 88583
1230363Sdas/*
2230363Sdas * Copyright (c) 1989, 1993, 1994
3230363Sdas *	The Regents of the University of California.  All rights reserved.
4230363Sdas *
5230363Sdas * This code is derived from software contributed to Berkeley by
6230363Sdas * Michael Fischbein.
7230363Sdas *
8230363Sdas * Redistribution and use in source and binary forms, with or without
9230363Sdas * modification, are permitted provided that the following conditions
10230363Sdas * are met:
11230363Sdas * 1. Redistributions of source code must retain the above copyright
12230363Sdas *    notice, this list of conditions and the following disclaimer.
13230363Sdas * 2. Redistributions in binary form must reproduce the above copyright
14230363Sdas *    notice, this list of conditions and the following disclaimer in the
15230363Sdas *    documentation and/or other materials provided with the distribution.
16230363Sdas * 3. All advertising materials mentioning features or use of this software
17230363Sdas *    must display the following acknowledgement:
18230363Sdas *	This product includes software developed by the University of
19230363Sdas *	California, Berkeley and its contributors.
20230363Sdas * 4. Neither the name of the University nor the names of its contributors
21230363Sdas *    may be used to endorse or promote products derived from this software
22230363Sdas *    without specific prior written permission.
23230363Sdas *
24230363Sdas * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25230363Sdas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26230363Sdas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27230363Sdas * 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 88583 2001-12-28 18:14:50Z 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
97char *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, chcnt, cnt, col, colwidth, num;
214	int endcol, numcols, numrows, row;
215	int tabwidth;
216
217	if (f_notabs)
218		tabwidth = 1;
219	else
220		tabwidth = 8;
221
222	/*
223	 * Have to do random access in the linked list -- build a table
224	 * of pointers.
225	 */
226	if (dp->entries > lastentries) {
227		lastentries = dp->entries;
228		if ((array =
229		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
230			warn(NULL);
231			printscol(dp);
232		}
233	}
234	for (p = dp->list, num = 0; p; p = p->fts_link)
235		if (p->fts_number != NO_PRINT)
236			array[num++] = p;
237
238	colwidth = dp->maxlen;
239	if (f_inode)
240		colwidth += dp->s_inode + 1;
241	if (f_size)
242		colwidth += dp->s_block + 1;
243	if (f_type)
244		colwidth += 1;
245
246	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
247	if (termwidth < 2 * colwidth) {
248		printscol(dp);
249		return;
250	}
251
252	numcols = termwidth / colwidth;
253	numrows = num / numcols;
254	if (num % numcols)
255		++numrows;
256
257	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
258		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
259	for (row = 0; row < numrows; ++row) {
260		endcol = colwidth;
261		for (base = row, chcnt = col = 0; col < numcols; ++col) {
262			chcnt += printaname(array[base], dp->s_inode,
263			    dp->s_block);
264			if ((base += numrows) >= num)
265				break;
266			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
267			    <= endcol){
268				(void)putchar(f_notabs ? ' ' : '\t');
269				chcnt = cnt;
270			}
271			endcol += colwidth;
272		}
273		(void)putchar('\n');
274	}
275}
276
277/*
278 * print [inode] [size] name
279 * return # of characters printed, no trailing characters.
280 */
281static int
282printaname(p, inodefield, sizefield)
283	FTSENT *p;
284	u_long sizefield, inodefield;
285{
286	struct stat *sp;
287	int chcnt;
288#ifdef COLORLS
289	int color_printed = 0;
290#endif
291
292	sp = p->fts_statp;
293	chcnt = 0;
294	if (f_inode)
295		chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
296	if (f_size)
297		chcnt += printf("%*qd ",
298		    (int)sizefield, howmany(sp->st_blocks, blocksize));
299#ifdef COLORLS
300	if (f_color)
301		color_printed = colortype(sp->st_mode);
302#endif
303	chcnt += printname(p->fts_name);
304#ifdef COLORLS
305	if (f_color && color_printed)
306		endcolor(0);
307#endif
308	if (f_type)
309		chcnt += printtype(sp->st_mode);
310	return (chcnt);
311}
312
313static void
314printtime(ftime)
315	time_t ftime;
316{
317	char longstring[80];
318	static time_t now;
319	const char *format;
320	static int d_first = -1;
321
322	if (d_first < 0)
323		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
324	if (now == 0)
325		now = time(NULL);
326
327#define	SIXMONTHS	((365 / 2) * 86400)
328	if (f_sectime)
329		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
330		format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
331	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
332		/* mmm dd hh:mm || dd mmm hh:mm */
333		format = d_first ? "%e %b %R " : "%b %e %R ";
334	else
335		/* mmm dd  yyyy || dd mmm  yyyy */
336		format = d_first ? "%e %b  %Y " : "%b %e  %Y ";
337	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
338	fputs(longstring, stdout);
339}
340
341static int
342printtype(mode)
343	u_int mode;
344{
345	switch (mode & S_IFMT) {
346	case S_IFDIR:
347		(void)putchar('/');
348		return (1);
349	case S_IFIFO:
350		(void)putchar('|');
351		return (1);
352	case S_IFLNK:
353		(void)putchar('@');
354		return (1);
355	case S_IFSOCK:
356		(void)putchar('=');
357		return (1);
358	case S_IFWHT:
359		(void)putchar('%');
360		return (1);
361	}
362	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
363		(void)putchar('*');
364		return (1);
365	}
366	return (0);
367}
368
369#ifdef COLORLS
370static int
371putch(c)
372	int c;
373{
374	(void) putchar(c);
375	return 0;
376}
377
378static int
379writech(c)
380	int c;
381{
382	char tmp = c;
383
384	(void) write(STDOUT_FILENO, &tmp, 1);
385	return 0;
386}
387
388static void
389printcolor(c)
390       Colors c;
391{
392	char *ansiseq;
393
394	if (colors[c].bold)
395		tputs(enter_bold, 1, putch);
396
397	if (colors[c].num[0] != -1) {
398		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
399		if (ansiseq)
400			tputs(ansiseq, 1, putch);
401	}
402
403	if (colors[c].num[1] != -1) {
404		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
405		if (ansiseq)
406			tputs(ansiseq, 1, putch);
407	}
408}
409
410static void
411endcolor(sig)
412	int sig;
413{
414	tputs(ansi_coloff, 1, sig ? writech : putch);
415	tputs(attrs_off, 1, sig ? writech : putch);
416}
417
418static int
419colortype(mode)
420       mode_t mode;
421{
422	switch(mode & S_IFMT) {
423	      case S_IFDIR:
424		if (mode & S_IWOTH)
425		    if (mode & S_ISTXT)
426			printcolor(C_WSDIR);
427		    else
428			printcolor(C_WDIR);
429		else
430		    printcolor(C_DIR);
431		return(1);
432	      case S_IFLNK:
433		printcolor(C_LNK);
434		return(1);
435	      case S_IFSOCK:
436		printcolor(C_SOCK);
437		return(1);
438	      case S_IFIFO:
439		printcolor(C_FIFO);
440		return(1);
441	      case S_IFBLK:
442		printcolor(C_BLK);
443		return(1);
444	      case S_IFCHR:
445		printcolor(C_CHR);
446		return(1);
447	}
448	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
449		if (mode & S_ISUID)
450		    printcolor(C_SUID);
451		else if (mode & S_ISGID)
452		    printcolor(C_SGID);
453		else
454		    printcolor(C_EXEC);
455		return(1);
456	}
457	return(0);
458}
459
460void
461parsecolors(cs)
462char *cs;
463{
464	int i, j, len;
465	char c[2];
466	short legacy_warn = 0;
467
468	if (cs == NULL)    cs = ""; /* LSCOLORS not set */
469	len = strlen(cs);
470	for (i = 0 ; i < C_NUMCOLORS ; i++) {
471		colors[i].bold = 0;
472
473		if (len <= 2*i) {
474			c[0] = defcolors[2*i];
475			c[1] = defcolors[2*i+1];
476		}
477		else {
478			c[0] = cs[2*i];
479			c[1] = cs[2*i+1];
480		}
481		for (j = 0 ; j < 2 ; j++) {
482			/* Legacy colours used 0-7 */
483			if (c[j] >= '0' && c[j] <= '7') {
484				colors[i].num[j] = c[j] - '0';
485				if (!legacy_warn) {
486					fprintf(stderr,
487					    "warn: colors are now defined "
488					    "using a-h instead of 0-9. "
489					    "see manual page.\n");
490				}
491				legacy_warn = 1;
492			} else if (c[j] >= 'a' && c[j] <= 'h')
493				colors[i].num[j] = c[j] - 'a';
494			else if (c[j] >= 'A' && c[j] <= 'H') {
495				colors[i].num[j] = c[j] - 'A';
496				colors[i].bold = 1;
497			} else if (tolower((unsigned char)c[j] == 'x'))
498				colors[i].num[j] = -1;
499			else {
500				fprintf(stderr,
501				    "error: invalid character '%c' in LSCOLORS"
502				    " env var\n", c[j]);
503				colors[i].num[j] = defcolors[2*i+j]-'0';
504			}
505		}
506	}
507}
508
509void
510colorquit(sig)
511	int sig;
512{
513	endcolor(sig);
514
515	(void) signal(sig, SIG_DFL);
516	(void) kill(getpid(), sig);
517}
518#endif /*COLORLS*/
519
520static void
521printlink(p)
522	FTSENT *p;
523{
524	int lnklen;
525	char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1];
526
527	if (p->fts_level == FTS_ROOTLEVEL)
528		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
529	else
530		(void)snprintf(name, sizeof(name),
531		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
532	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
533		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
534		return;
535	}
536	path[lnklen] = '\0';
537	(void)printf(" -> ");
538	printname(path);
539}
540