xref: /freebsd/bin/ls/print.c (revision c4f6a2a9e1b1879b618c436ab4f56ff75c73a0f5)
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 #if 0
38 #ifndef lint
39 static char sccsid[] = "@(#)print.c	8.4 (Berkeley) 4/17/94";
40 #endif /* not lint */
41 #endif
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD$");
44 
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 
48 #include <err.h>
49 #include <errno.h>
50 #include <fts.h>
51 #include <math.h>
52 #include <langinfo.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58 #ifdef COLORLS
59 #include <ctype.h>
60 #include <termcap.h>
61 #include <signal.h>
62 #endif
63 
64 #include "ls.h"
65 #include "extern.h"
66 
67 static int	printaname(FTSENT *, u_long, u_long);
68 static void	printlink(FTSENT *);
69 static void	printtime(time_t);
70 static int	printtype(u_int);
71 static void	printsize(size_t, off_t);
72 #ifdef COLORLS
73 static void	endcolor(int);
74 static int	colortype(mode_t);
75 #endif
76 
77 #define	IS_NOPRINT(p)	((p)->fts_number == NO_PRINT)
78 
79 #define KILO_SZ(n) (n)
80 #define MEGA_SZ(n) ((n) * (n))
81 #define GIGA_SZ(n) ((n) * (n) * (n))
82 #define TERA_SZ(n) ((n) * (n) * (n) * (n))
83 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
84 
85 #define KILO_2_SZ (KILO_SZ(1024ULL))
86 #define MEGA_2_SZ (MEGA_SZ(1024ULL))
87 #define GIGA_2_SZ (GIGA_SZ(1024ULL))
88 #define TERA_2_SZ (TERA_SZ(1024ULL))
89 #define PETA_2_SZ (PETA_SZ(1024ULL))
90 
91 static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
92 
93 typedef enum {
94 	NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX
95 } unit_t;
96 static unit_t unit_adjust(off_t *);
97 
98 static int unitp[] = {NONE, KILO, MEGA, GIGA, TERA, PETA};
99 
100 #ifdef COLORLS
101 /* Most of these are taken from <sys/stat.h> */
102 typedef enum Colors {
103 	C_DIR,			/* directory */
104 	C_LNK,			/* symbolic link */
105 	C_SOCK,			/* socket */
106 	C_FIFO,			/* pipe */
107 	C_EXEC,			/* executable */
108 	C_BLK,			/* block special */
109 	C_CHR,			/* character special */
110 	C_SUID,			/* setuid executable */
111 	C_SGID,			/* setgid executable */
112 	C_WSDIR,		/* directory writeble to others, with sticky
113 				 * bit */
114 	C_WDIR,			/* directory writeble to others, without
115 				 * sticky bit */
116 	C_NUMCOLORS		/* just a place-holder */
117 } Colors;
118 
119 static const char *defcolors = "exfxcxdxbxegedabagacad";
120 
121 /* colors for file types */
122 static struct {
123 	int	num[2];
124 	int	bold;
125 } colors[C_NUMCOLORS];
126 #endif
127 
128 void
129 printscol(DISPLAY *dp)
130 {
131 	FTSENT *p;
132 
133 	for (p = dp->list; p; p = p->fts_link) {
134 		if (IS_NOPRINT(p))
135 			continue;
136 		(void)printaname(p, dp->s_inode, dp->s_block);
137 		(void)putchar('\n');
138 	}
139 }
140 
141 /*
142  * print name in current style
143  */
144 static int
145 printname(const char *name)
146 {
147 	if (f_octal || f_octal_escape)
148 		return prn_octal(name);
149 	else if (f_nonprint)
150 		return prn_printable(name);
151 	else
152 		return printf("%s", name);
153 }
154 
155 void
156 printlong(DISPLAY *dp)
157 {
158 	struct stat *sp;
159 	FTSENT *p;
160 	NAMES *np;
161 	char buf[20];
162 #ifdef COLORLS
163 	int color_printed = 0;
164 #endif
165 
166 	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
167 		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
168 
169 	for (p = dp->list; p; p = p->fts_link) {
170 		if (IS_NOPRINT(p))
171 			continue;
172 		sp = p->fts_statp;
173 		if (f_inode)
174 			(void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
175 		if (f_size)
176 			(void)printf("%*lld ",
177 			    dp->s_block, howmany(sp->st_blocks, blocksize));
178 		strmode(sp->st_mode, buf);
179 		np = p->fts_pointer;
180 		(void)printf("%s %*u %-*s  %-*s  ", buf, dp->s_nlink,
181 		    sp->st_nlink, dp->s_user, np->user, dp->s_group,
182 		    np->group);
183 		if (f_flags)
184 			(void)printf("%-*s ", dp->s_flags, np->flags);
185 		if (f_lomac)
186 			(void)printf("%-*s ", dp->s_lattr, np->lattr);
187 		if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
188 			if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
189 				(void)printf("%3d, 0x%08x ",
190 				    major(sp->st_rdev),
191 				    (u_int)minor(sp->st_rdev));
192 			else
193 				(void)printf("%3d, %3d ",
194 				    major(sp->st_rdev), minor(sp->st_rdev));
195 		else if (dp->bcfile)
196 			(void)printf("%*s%*lld ",
197 			    8 - dp->s_size, "", dp->s_size, sp->st_size);
198 		else
199 			printsize(dp->s_size, sp->st_size);
200 		if (f_accesstime)
201 			printtime(sp->st_atime);
202 		else if (f_statustime)
203 			printtime(sp->st_ctime);
204 		else
205 			printtime(sp->st_mtime);
206 #ifdef COLORLS
207 		if (f_color)
208 			color_printed = colortype(sp->st_mode);
209 #endif
210 		(void)printname(p->fts_name);
211 #ifdef COLORLS
212 		if (f_color && color_printed)
213 			endcolor(0);
214 #endif
215 		if (f_type)
216 			(void)printtype(sp->st_mode);
217 		if (S_ISLNK(sp->st_mode))
218 			printlink(p);
219 		(void)putchar('\n');
220 	}
221 }
222 
223 void
224 printstream(DISPLAY *dp)
225 {
226 	FTSENT *p;
227 	extern int termwidth;
228 	int chcnt;
229 
230 	for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
231 		if (p->fts_number == NO_PRINT)
232 			continue;
233 		if (strlen(p->fts_name) + chcnt +
234 		    (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
235 			putchar('\n');
236 			chcnt = 0;
237 		}
238 		chcnt += printaname(p, dp->s_inode, dp->s_block);
239 		if (p->fts_link) {
240 			printf(", ");
241 			chcnt += 2;
242 		}
243 	}
244 	if (chcnt)
245 		putchar('\n');
246 }
247 
248 void
249 printcol(DISPLAY *dp)
250 {
251 	extern int termwidth;
252 	static FTSENT **array;
253 	static int lastentries = -1;
254 	FTSENT *p;
255 	int base;
256 	int chcnt;
257 	int cnt;
258 	int col;
259 	int colwidth;
260 	int endcol;
261 	int num;
262 	int numcols;
263 	int numrows;
264 	int row;
265 	int tabwidth;
266 
267 	if (f_notabs)
268 		tabwidth = 1;
269 	else
270 		tabwidth = 8;
271 
272 	/*
273 	 * Have to do random access in the linked list -- build a table
274 	 * of pointers.
275 	 */
276 	if (dp->entries > lastentries) {
277 		lastentries = dp->entries;
278 		if ((array =
279 		    realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
280 			warn(NULL);
281 			printscol(dp);
282 		}
283 	}
284 	for (p = dp->list, num = 0; p; p = p->fts_link)
285 		if (p->fts_number != NO_PRINT)
286 			array[num++] = p;
287 
288 	colwidth = dp->maxlen;
289 	if (f_inode)
290 		colwidth += dp->s_inode + 1;
291 	if (f_size)
292 		colwidth += dp->s_block + 1;
293 	if (f_type)
294 		colwidth += 1;
295 
296 	colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
297 	if (termwidth < 2 * colwidth) {
298 		printscol(dp);
299 		return;
300 	}
301 	numcols = termwidth / colwidth;
302 	numrows = num / numcols;
303 	if (num % numcols)
304 		++numrows;
305 
306 	if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
307 		(void)printf("total %lu\n", howmany(dp->btotal, blocksize));
308 
309 	if (f_sortacross)
310 		base = 0;
311 	for (row = 0; row < numrows; ++row) {
312 		endcol = colwidth;
313 		if (!f_sortacross)
314 			base = row;
315 		for (col = 0, chcnt = 0; col < numcols; ++col) {
316 			chcnt += printaname(array[base], dp->s_inode,
317 			    dp->s_block);
318 			if (f_sortacross)
319 				base++;
320 			else
321 				base += numrows;
322 			if (base >= num)
323 				break;
324 			while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
325 			    <= endcol) {
326 				if (f_sortacross && col + 1 >= numcols)
327 					break;
328 				(void)putchar(f_notabs ? ' ' : '\t');
329 				chcnt = cnt;
330 			}
331 			endcol += colwidth;
332 		}
333 		(void)putchar('\n');
334 	}
335 }
336 
337 /*
338  * print [inode] [size] name
339  * return # of characters printed, no trailing characters.
340  */
341 static int
342 printaname(FTSENT *p, u_long inodefield, u_long sizefield)
343 {
344 	struct stat *sp;
345 	int chcnt;
346 #ifdef COLORLS
347 	int color_printed = 0;
348 #endif
349 
350 	sp = p->fts_statp;
351 	chcnt = 0;
352 	if (f_inode)
353 		chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
354 	if (f_size)
355 		chcnt += printf("%*lld ",
356 		    (int)sizefield, howmany(sp->st_blocks, blocksize));
357 #ifdef COLORLS
358 	if (f_color)
359 		color_printed = colortype(sp->st_mode);
360 #endif
361 	chcnt += printname(p->fts_name);
362 #ifdef COLORLS
363 	if (f_color && color_printed)
364 		endcolor(0);
365 #endif
366 	if (f_type)
367 		chcnt += printtype(sp->st_mode);
368 	return (chcnt);
369 }
370 
371 static void
372 printtime(time_t ftime)
373 {
374 	char longstring[80];
375 	static time_t now;
376 	const char *format;
377 	static int d_first = -1;
378 
379 	if (d_first < 0)
380 		d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
381 	if (now == 0)
382 		now = time(NULL);
383 
384 #define	SIXMONTHS	((365 / 2) * 86400)
385 	if (f_sectime)
386 		/* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
387 		format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
388 	else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
389 		/* mmm dd hh:mm || dd mmm hh:mm */
390 		format = d_first ? "%e %b %R " : "%b %e %R ";
391 	else
392 		/* mmm dd  yyyy || dd mmm  yyyy */
393 		format = d_first ? "%e %b  %Y " : "%b %e  %Y ";
394 	strftime(longstring, sizeof(longstring), format, localtime(&ftime));
395 	fputs(longstring, stdout);
396 }
397 
398 static int
399 printtype(u_int mode)
400 {
401 
402 	if (f_slash) {
403 		if ((mode & S_IFMT) == S_IFDIR) {
404 			(void)putchar('/');
405 			return (1);
406 		}
407 		return (0);
408 	}
409 
410 	switch (mode & S_IFMT) {
411 	case S_IFDIR:
412 		(void)putchar('/');
413 		return (1);
414 	case S_IFIFO:
415 		(void)putchar('|');
416 		return (1);
417 	case S_IFLNK:
418 		(void)putchar('@');
419 		return (1);
420 	case S_IFSOCK:
421 		(void)putchar('=');
422 		return (1);
423 	case S_IFWHT:
424 		(void)putchar('%');
425 		return (1);
426 	default:
427 		break;
428 	}
429 	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
430 		(void)putchar('*');
431 		return (1);
432 	}
433 	return (0);
434 }
435 
436 #ifdef COLORLS
437 static int
438 putch(int c)
439 {
440 	(void)putchar(c);
441 	return 0;
442 }
443 
444 static int
445 writech(int c)
446 {
447 	char tmp = c;
448 
449 	(void)write(STDOUT_FILENO, &tmp, 1);
450 	return 0;
451 }
452 
453 static void
454 printcolor(Colors c)
455 {
456 	char *ansiseq;
457 
458 	if (colors[c].bold)
459 		tputs(enter_bold, 1, putch);
460 
461 	if (colors[c].num[0] != -1) {
462 		ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
463 		if (ansiseq)
464 			tputs(ansiseq, 1, putch);
465 	}
466 	if (colors[c].num[1] != -1) {
467 		ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
468 		if (ansiseq)
469 			tputs(ansiseq, 1, putch);
470 	}
471 }
472 
473 static void
474 endcolor(int sig)
475 {
476 	tputs(ansi_coloff, 1, sig ? writech : putch);
477 	tputs(attrs_off, 1, sig ? writech : putch);
478 }
479 
480 static int
481 colortype(mode_t mode)
482 {
483 	switch (mode & S_IFMT) {
484 	case S_IFDIR:
485 		if (mode & S_IWOTH)
486 			if (mode & S_ISTXT)
487 				printcolor(C_WSDIR);
488 			else
489 				printcolor(C_WDIR);
490 		else
491 			printcolor(C_DIR);
492 		return (1);
493 	case S_IFLNK:
494 		printcolor(C_LNK);
495 		return (1);
496 	case S_IFSOCK:
497 		printcolor(C_SOCK);
498 		return (1);
499 	case S_IFIFO:
500 		printcolor(C_FIFO);
501 		return (1);
502 	case S_IFBLK:
503 		printcolor(C_BLK);
504 		return (1);
505 	case S_IFCHR:
506 		printcolor(C_CHR);
507 		return (1);
508 	}
509 	if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
510 		if (mode & S_ISUID)
511 			printcolor(C_SUID);
512 		else if (mode & S_ISGID)
513 			printcolor(C_SGID);
514 		else
515 			printcolor(C_EXEC);
516 		return (1);
517 	}
518 	return (0);
519 }
520 
521 void
522 parsecolors(const char *cs)
523 {
524 	int i;
525 	int j;
526 	int len;
527 	char c[2];
528 	short legacy_warn = 0;
529 
530 	if (cs == NULL)
531 		cs = "";	/* LSCOLORS not set */
532 	len = strlen(cs);
533 	for (i = 0; i < C_NUMCOLORS; i++) {
534 		colors[i].bold = 0;
535 
536 		if (len <= 2 * i) {
537 			c[0] = defcolors[2 * i];
538 			c[1] = defcolors[2 * i + 1];
539 		} else {
540 			c[0] = cs[2 * i];
541 			c[1] = cs[2 * i + 1];
542 		}
543 		for (j = 0; j < 2; j++) {
544 			/* Legacy colours used 0-7 */
545 			if (c[j] >= '0' && c[j] <= '7') {
546 				colors[i].num[j] = c[j] - '0';
547 				if (!legacy_warn) {
548 					fprintf(stderr,
549 					    "warn: LSCOLORS should use "
550 					    "characters a-h instead of 0-9 ("
551 					    "see the manual page)\n");
552 				}
553 				legacy_warn = 1;
554 			} else if (c[j] >= 'a' && c[j] <= 'h')
555 				colors[i].num[j] = c[j] - 'a';
556 			else if (c[j] >= 'A' && c[j] <= 'H') {
557 				colors[i].num[j] = c[j] - 'A';
558 				colors[i].bold = 1;
559 			} else if (tolower((unsigned char)c[j] == 'x'))
560 				colors[i].num[j] = -1;
561 			else {
562 				fprintf(stderr,
563 				    "error: invalid character '%c' in LSCOLORS"
564 				    " env var\n", c[j]);
565 				colors[i].num[j] = -1;
566 			}
567 		}
568 	}
569 }
570 
571 void
572 colorquit(int sig)
573 {
574 	endcolor(sig);
575 
576 	(void)signal(sig, SIG_DFL);
577 	(void)kill(getpid(), sig);
578 }
579 
580 #endif /* COLORLS */
581 
582 static void
583 printlink(FTSENT *p)
584 {
585 	int lnklen;
586 	char name[MAXPATHLEN + 1];
587 	char path[MAXPATHLEN + 1];
588 
589 	if (p->fts_level == FTS_ROOTLEVEL)
590 		(void)snprintf(name, sizeof(name), "%s", p->fts_name);
591 	else
592 		(void)snprintf(name, sizeof(name),
593 		    "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
594 	if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
595 		(void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
596 		return;
597 	}
598 	path[lnklen] = '\0';
599 	(void)printf(" -> ");
600 	(void)printname(path);
601 }
602 
603 static void
604 printsize(size_t width, off_t bytes)
605 {
606 	unit_t unit;
607 
608 	if (f_humanval) {
609 		unit = unit_adjust(&bytes);
610 
611 		if (bytes == 0)
612 			(void)printf("%*s ", width, "0B");
613 		else
614 			(void)printf("%*lld%c ", width - 1, bytes,
615 			    "BKMGTPE"[unit]);
616 	} else
617 		(void)printf("%*lld ", width, bytes);
618 }
619 
620 /*
621  * Output in "human-readable" format.  Uses 3 digits max and puts
622  * unit suffixes at the end.  Makes output compact and easy to read,
623  * especially on huge disks.
624  *
625  */
626 unit_t
627 unit_adjust(off_t *val)
628 {
629 	double abval;
630 	unit_t unit;
631 	unsigned int unit_sz;
632 
633 	abval = fabs((double)*val);
634 
635 	unit_sz = abval ? ilogb(abval) / 10 : 0;
636 
637 	if (unit_sz >= UNIT_MAX) {
638 		unit = NONE;
639 	} else {
640 		unit = unitp[unit_sz];
641 		*val /= (double)vals_base2[unit_sz];
642 	}
643 
644 	return (unit);
645 }
646