print.c revision 88602
10SN/A/*
20SN/A * Copyright (c) 1989, 1993, 1994
30SN/A *	The Regents of the University of California.  All rights reserved.
40SN/A *
50SN/A * This code is derived from software contributed to Berkeley by
61133Sjoehw * Michael Fischbein.
71133Sjoehw *
81133Sjoehw * Redistribution and use in source and binary forms, with or without
91133Sjoehw * modification, are permitted provided that the following conditions
101133Sjoehw * are met:
111133Sjoehw * 1. Redistributions of source code must retain the above copyright
120SN/A *    notice, this list of conditions and the following disclaimer.
130SN/A * 2. Redistributions in binary form must reproduce the above copyright
140SN/A *    notice, this list of conditions and the following disclaimer in the
150SN/A *    documentation and/or other materials provided with the distribution.
160SN/A * 3. All advertising materials mentioning features or use of this software
170SN/A *    must display the following acknowledgement:
180SN/A *	This product includes software developed by the University of
190SN/A *	California, Berkeley and its contributors.
200SN/A * 4. Neither the name of the University nor the names of its contributors
210SN/A *    may be used to endorse or promote products derived from this software
220SN/A *    without specific prior written permission.
230SN/A *
24405SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25405SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
260SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
270SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
280SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
290SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
300SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
310SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
320SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
330SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
340SN/A * SUCH DAMAGE.
350SN/A */
360SN/A
370SN/A#ifndef lint
380SN/A#if 0
390SN/Astatic char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
400SN/A#else
410SN/Astatic const char rcsid[] =
420SN/A  "$FreeBSD: head/bin/ls/print.c 88602 2001-12-29 00:18:16Z joe $";
430SN/A#endif
440SN/A#endif /* not lint */
450SN/A
460SN/A#include <sys/param.h>
470SN/A#include <sys/stat.h>
480SN/A
490SN/A#include <err.h>
500SN/A#include <errno.h>
510SN/A#include <fts.h>
520SN/A#include <grp.h>
530SN/A#include <math.h>
540SN/A#include <langinfo.h>
550SN/A#include <pwd.h>
560SN/A#include <stdio.h>
570SN/A#include <stdlib.h>
580SN/A#include <string.h>
590SN/A#include <time.h>
600SN/A#include <unistd.h>
610SN/A#ifdef COLORLS
620SN/A#include <ctype.h>
630SN/A#include <termcap.h>
640SN/A#include <signal.h>
65405SN/A#endif
660SN/A
670SN/A#include "ls.h"
680SN/A#include "extern.h"
690SN/A
70405SN/Astatic int	printaname __P((FTSENT *, u_long, u_long));
710SN/Astatic void	printlink __P((FTSENT *));
720SN/Astatic void	printtime __P((time_t));
730SN/Astatic int	printtype __P((u_int));
740SN/Astatic void	printsize __P((size_t, off_t));
750SN/A#ifdef COLORLS
760SN/Astatic void	endcolor __P((int));
770SN/Astatic int	colortype __P((mode_t));
780SN/A#endif
790SN/A
800SN/A#define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
810SN/A#define UNITS_2 2
820SN/A
830SN/A#define KILO_SZ(n) (n)
840SN/A#define MEGA_SZ(n) ((n) * (n))
850SN/A#define GIGA_SZ(n) ((n) * (n) * (n))
860SN/A#define TERA_SZ(n) ((n) * (n) * (n) * (n))
870SN/A#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
880SN/A
890SN/A#define KILO_2_SZ (KILO_SZ(1024ULL))
900SN/A#define MEGA_2_SZ (MEGA_SZ(1024ULL))
91#define GIGA_2_SZ (GIGA_SZ(1024ULL))
92#define TERA_2_SZ (TERA_SZ(1024ULL))
93#define PETA_2_SZ (PETA_SZ(1024ULL))
94
95unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
96
97typedef enum {
98	NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX
99} unit_t;
100static unit_t unit_adjust __P((off_t *));
101
102int unitp[] = {NONE, KILO, MEGA, GIGA, TERA, PETA};
103
104#ifdef COLORLS
105/* Most of these are taken from <sys/stat.h> */
106typedef enum Colors {
107	C_DIR,			/* directory */
108	C_LNK,			/* symbolic link */
109	C_SOCK,			/* socket */
110	C_FIFO,			/* pipe */
111	C_EXEC,			/* executable */
112	C_BLK,			/* block special */
113	C_CHR,			/* character special */
114	C_SUID,			/* setuid executable */
115	C_SGID,			/* setgid executable */
116	C_WSDIR,		/* directory writeble to others, with sticky
117				 * bit */
118	C_WDIR,			/* directory writeble to others, without
119				 * sticky bit */
120	C_NUMCOLORS		/* just a place-holder */
121} Colors;
122
123const char *defcolors = "exfxcxdxbxegedabagacad";
124
125/* colors for file types */
126static struct {
127	int	num[2];
128	int	bold;
129} colors[C_NUMCOLORS];
130#endif
131
132void
133printscol(dp)
134	DISPLAY *dp;
135{
136	FTSENT *p;
137
138	for (p = dp->list; p; p = p->fts_link) {
139		if (IS_NOPRINT(p))
140			continue;
141		(void)printaname(p, dp->s_inode, dp->s_block);
142		(void)putchar('\n');
143	}
144}
145
146/*
147 * print name in current style
148 */
149static int
150printname(name)
151	const char *name;
152{
153	if (f_octal || f_octal_escape)
154		return prn_octal(name);
155	else if (f_nonprint)
156		return prn_printable(name);
157	else
158		return printf("%s", name);
159}
160
161void
162printlong(dp)
163	DISPLAY *dp;
164{
165	struct stat *sp;
166	FTSENT *p;
167	NAMES *np;
168	char buf[20];
169#ifdef COLORLS
170	int color_printed = 0;
171#endif
172
173	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
174		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
175
176	for (p = dp->list; p; p = p->fts_link) {
177		if (IS_NOPRINT(p))
178			continue;
179		sp = p->fts_statp;
180		if (f_inode)
181			(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
182		if (f_size)
183			(void)printf("%*qd ",
184			    dp->s_block, howmany(sp->st_blocks, blocksize));
185		(void)strmode(sp->st_mode, buf);
186		np = p->fts_pointer;
187		(void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
188		    sp->st_nlink, dp->s_user, np->user, dp->s_group,
189		    np->group);
190		if (f_flags)
191			(void)printf("%-*s ", dp->s_flags, np->flags);
192		if (f_lomac)
193			(void)printf("%-*s ", dp->s_lattr, np->lattr);
194		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
195			if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
196				(void)printf("%3d, 0x%08x ",
197				    major(sp->st_rdev),
198				    (u_int)minor(sp->st_rdev));
199			else
200				(void)printf("%3d, %3d ",
201				    major(sp->st_rdev), minor(sp->st_rdev));
202		else if (dp->bcfile)
203			(void)printf("%*s%*qd ",
204			    8 - dp->s_size, "", dp->s_size, sp->st_size);
205		else
206			printsize(dp->s_size, sp->st_size);
207		if (f_accesstime)
208			printtime(sp->st_atime);
209		else if (f_statustime)
210			printtime(sp->st_ctime);
211		else
212			printtime(sp->st_mtime);
213#ifdef COLORLS
214		if (f_color)
215			color_printed = colortype(sp->st_mode);
216#endif
217		(void)printname(p->fts_name);
218#ifdef COLORLS
219		if (f_color && color_printed)
220			endcolor(0);
221#endif
222		if (f_type)
223			(void)printtype(sp->st_mode);
224		if (S_ISLNK(sp->st_mode))
225			printlink(p);
226		(void)putchar('\n');
227	}
228}
229
230void
231printcol(dp)
232	DISPLAY *dp;
233{
234	extern int termwidth;
235	static FTSENT **array;
236	static int lastentries = -1;
237	FTSENT *p;
238	int base;
239	int chcnt;
240	int cnt;
241	int col;
242	int colwidth;
243	int endcol;
244	int num;
245	int numcols;
246	int numrows;
247	int row;
248	int tabwidth;
249
250	if (f_notabs)
251		tabwidth = 1;
252	else
253		tabwidth = 8;
254
255	/*
256	 * Have to do random access in the linked list -- build a table
257	 * of pointers.
258	 */
259	if (dp->entries > lastentries) {
260		lastentries = dp->entries;
261		if ((array =
262		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
263			warn(NULL);
264			printscol(dp);
265		}
266	}
267	for (p = dp->list, num = 0; p; p = p->fts_link)
268		if (p->fts_number != NO_PRINT)
269			array[num++] = p;
270
271	colwidth = dp->maxlen;
272	if (f_inode)
273		colwidth += dp->s_inode + 1;
274	if (f_size)
275		colwidth += dp->s_block + 1;
276	if (f_type)
277		colwidth += 1;
278
279	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
280	if (termwidth < 2 * colwidth) {
281		printscol(dp);
282		return;
283	}
284	numcols = termwidth / colwidth;
285	numrows = num / numcols;
286	if (num % numcols)
287		++numrows;
288
289	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
290		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
291	for (row = 0; row < numrows; ++row) {
292		endcol = colwidth;
293		for (base = row, chcnt = col = 0; col < numcols; ++col) {
294			chcnt += printaname(array[base], dp->s_inode,
295			    dp->s_block);
296			if ((base += numrows) >= num)
297				break;
298			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
299			    <= endcol) {
300				(void)putchar(f_notabs ? ' ' : '\t');
301				chcnt = cnt;
302			}
303			endcol += colwidth;
304		}
305		(void)putchar('\n');
306	}
307}
308
309/*
310 * print [inode] [size] name
311 * return # of characters printed, no trailing characters.
312 */
313static int
314printaname(p, inodefield, sizefield)
315	FTSENT *p;
316	u_long inodefield;
317	u_long sizefield;
318{
319	struct stat *sp;
320	int chcnt;
321#ifdef COLORLS
322	int color_printed = 0;
323#endif
324
325	sp = p->fts_statp;
326	chcnt = 0;
327	if (f_inode)
328		chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
329	if (f_size)
330		chcnt += printf("%*qd ",
331		    (int)sizefield, howmany(sp->st_blocks, blocksize));
332#ifdef COLORLS
333	if (f_color)
334		color_printed = colortype(sp->st_mode);
335#endif
336	chcnt += printname(p->fts_name);
337#ifdef COLORLS
338	if (f_color && color_printed)
339		endcolor(0);
340#endif
341	if (f_type)
342		chcnt += printtype(sp->st_mode);
343	return (chcnt);
344}
345
346static void
347printtime(ftime)
348	time_t ftime;
349{
350	char longstring[80];
351	static time_t now;
352	const char *format;
353	static int d_first = -1;
354
355	if (d_first < 0)
356		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
357	if (now == 0)
358		now = time(NULL);
359
360#define	SIXMONTHS	((365 / 2) * 86400)
361	if (f_sectime)
362		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
363		format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
364	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
365		/* mmm dd hh:mm || dd mmm hh:mm */
366		format = d_first ? "%e %b %R " : "%b %e %R ";
367	else
368		/* mmm dd  yyyy || dd mmm  yyyy */
369		format = d_first ? "%e %b  %Y " : "%b %e  %Y ";
370	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
371	fputs(longstring, stdout);
372}
373
374static int
375printtype(mode)
376	u_int mode;
377{
378	switch (mode & S_IFMT) {
379	case S_IFDIR:
380		(void)putchar('/');
381		return (1);
382	case S_IFIFO:
383		(void)putchar('|');
384		return (1);
385	case S_IFLNK:
386		(void)putchar('@');
387		return (1);
388	case S_IFSOCK:
389		(void)putchar('=');
390		return (1);
391	case S_IFWHT:
392		(void)putchar('%');
393		return (1);
394	}
395	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
396		(void)putchar('*');
397		return (1);
398	}
399	return (0);
400}
401
402#ifdef COLORLS
403static int
404putch(c)
405	int c;
406{
407	(void)putchar(c);
408	return 0;
409}
410
411static int
412writech(c)
413	int c;
414{
415	char tmp = c;
416
417	(void)write(STDOUT_FILENO, &tmp, 1);
418	return 0;
419}
420
421static void
422printcolor(c)
423	Colors c;
424{
425	char *ansiseq;
426
427	if (colors[c].bold)
428		tputs(enter_bold, 1, putch);
429
430	if (colors[c].num[0] != -1) {
431		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
432		if (ansiseq)
433			tputs(ansiseq, 1, putch);
434	}
435	if (colors[c].num[1] != -1) {
436		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
437		if (ansiseq)
438			tputs(ansiseq, 1, putch);
439	}
440}
441
442static void
443endcolor(sig)
444	int sig;
445{
446	tputs(ansi_coloff, 1, sig ? writech : putch);
447	tputs(attrs_off, 1, sig ? writech : putch);
448}
449
450static int
451colortype(mode)
452	mode_t mode;
453{
454	switch (mode & S_IFMT) {
455	case S_IFDIR:
456		if (mode & S_IWOTH)
457			if (mode & S_ISTXT)
458				printcolor(C_WSDIR);
459			else
460				printcolor(C_WDIR);
461		else
462			printcolor(C_DIR);
463		return (1);
464	case S_IFLNK:
465		printcolor(C_LNK);
466		return (1);
467	case S_IFSOCK:
468		printcolor(C_SOCK);
469		return (1);
470	case S_IFIFO:
471		printcolor(C_FIFO);
472		return (1);
473	case S_IFBLK:
474		printcolor(C_BLK);
475		return (1);
476	case S_IFCHR:
477		printcolor(C_CHR);
478		return (1);
479	}
480	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
481		if (mode & S_ISUID)
482			printcolor(C_SUID);
483		else if (mode & S_ISGID)
484			printcolor(C_SGID);
485		else
486			printcolor(C_EXEC);
487		return (1);
488	}
489	return (0);
490}
491
492void
493parsecolors(cs)
494	const char *cs;
495{
496	int i;
497	int j;
498	int len;
499	char c[2];
500	short legacy_warn = 0;
501
502	if (cs == NULL)
503		cs = "";	/* LSCOLORS not set */
504	len = strlen(cs);
505	for (i = 0; i < C_NUMCOLORS; i++) {
506		colors[i].bold = 0;
507
508		if (len <= 2 * i) {
509			c[0] = defcolors[2 * i];
510			c[1] = defcolors[2 * i + 1];
511		} else {
512			c[0] = cs[2 * i];
513			c[1] = cs[2 * i + 1];
514		}
515		for (j = 0; j < 2; j++) {
516			/* Legacy colours used 0-7 */
517			if (c[j] >= '0' && c[j] <= '7') {
518				colors[i].num[j] = c[j] - '0';
519				if (!legacy_warn) {
520					fprintf(stderr,
521					    "warn: LSCOLOURS should use "
522					    "characters a-h instead of 0-9 ("
523					    "see the manual page)\n");
524				}
525				legacy_warn = 1;
526			} else if (c[j] >= 'a' && c[j] <= 'h')
527				colors[i].num[j] = c[j] - 'a';
528			else if (c[j] >= 'A' && c[j] <= 'H') {
529				colors[i].num[j] = c[j] - 'A';
530				colors[i].bold = 1;
531			} else if (tolower((unsigned char)c[j] == 'x'))
532				colors[i].num[j] = -1;
533			else {
534				fprintf(stderr,
535				    "error: invalid character '%c' in LSCOLORS"
536				    " env var\n", c[j]);
537				colors[i].num[j] = -1;
538			}
539		}
540	}
541}
542
543void
544colorquit(sig)
545	int sig;
546{
547	endcolor(sig);
548
549	(void)signal(sig, SIG_DFL);
550	(void)kill(getpid(), sig);
551}
552
553#endif /* COLORLS */
554
555static void
556printlink(p)
557	FTSENT *p;
558{
559	int lnklen;
560	char name[MAXPATHLEN + 1];
561	char path[MAXPATHLEN + 1];
562
563	if (p->fts_level == FTS_ROOTLEVEL)
564		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
565	else
566		(void)snprintf(name, sizeof(name),
567		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
568	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
569		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
570		return;
571	}
572	path[lnklen] = '\0';
573	(void)printf(" -> ");
574	printname(path);
575}
576
577static void
578printsize(width, bytes)
579	size_t width;
580	off_t bytes;
581{
582	unit_t unit;
583
584	if (f_humanval) {
585		unit = unit_adjust(&bytes);
586
587		if (bytes == 0)
588			(void)printf("%*s ", width, "0B");
589		else
590			(void)printf("%*qd%c ", width - 1, bytes,
591			    "BKMGTPE"[unit]);
592	} else
593		(void)printf("%*qd ", width, bytes);
594}
595
596/*
597 * Output in "human-readable" format.  Uses 3 digits max and puts
598 * unit suffixes at the end.  Makes output compact and easy to read,
599 * especially on huge disks.
600 *
601 */
602unit_t
603unit_adjust(val)
604	off_t *val;
605{
606	double abval;
607	unit_t unit;
608	unsigned int unit_sz;
609
610	abval = fabs(*val);
611
612	unit_sz = abval ? ilogb(abval) / 10 : 0;
613
614	if (unit_sz >= UNIT_MAX) {
615		unit = NONE;
616	} else {
617		unit = unitp[unit_sz];
618		*val /= (double)vals_base2[unit_sz];
619	}
620
621	return (unit);
622}
623