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