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