print.c revision 127958
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 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#if 0 34#ifndef lint 35static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; 36#endif /* not lint */ 37#endif 38#include <sys/cdefs.h> 39__FBSDID("$FreeBSD: head/bin/ls/print.c 127958 2004-04-06 20:06:54Z markm $"); 40 41#include <sys/param.h> 42#include <sys/stat.h> 43#include <sys/acl.h> 44 45#include <err.h> 46#include <errno.h> 47#include <fts.h> 48#include <math.h> 49#include <langinfo.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <time.h> 54#include <unistd.h> 55#ifdef COLORLS 56#include <ctype.h> 57#include <termcap.h> 58#include <signal.h> 59#endif 60 61#include "ls.h" 62#include "extern.h" 63 64static int printaname(const FTSENT *, u_long, u_long); 65static void printlink(const FTSENT *); 66static void printtime(time_t); 67static int printtype(u_int); 68static void printsize(size_t, off_t); 69#ifdef COLORLS 70static void endcolor(int); 71static int colortype(mode_t); 72#endif 73static void aclmode(char *, const FTSENT *, int *); 74 75#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 76 77#define KILO_SZ(n) (n) 78#define MEGA_SZ(n) ((n) * (n)) 79#define GIGA_SZ(n) ((n) * (n) * (n)) 80#define TERA_SZ(n) ((n) * (n) * (n) * (n)) 81#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 82 83#define KILO_2_SZ (KILO_SZ(1024ULL)) 84#define MEGA_2_SZ (MEGA_SZ(1024ULL)) 85#define GIGA_2_SZ (GIGA_SZ(1024ULL)) 86#define TERA_2_SZ (TERA_SZ(1024ULL)) 87#define PETA_2_SZ (PETA_SZ(1024ULL)) 88 89static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 90 91typedef enum { 92 NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX 93} unit_t; 94static unit_t unit_adjust(double *); 95 96static unit_t unitp[] = {NONE, KILO, MEGA, GIGA, TERA, PETA}; 97 98#ifdef COLORLS 99/* Most of these are taken from <sys/stat.h> */ 100typedef enum Colors { 101 C_DIR, /* directory */ 102 C_LNK, /* symbolic link */ 103 C_SOCK, /* socket */ 104 C_FIFO, /* pipe */ 105 C_EXEC, /* executable */ 106 C_BLK, /* block special */ 107 C_CHR, /* character special */ 108 C_SUID, /* setuid executable */ 109 C_SGID, /* setgid executable */ 110 C_WSDIR, /* directory writeble to others, with sticky 111 * bit */ 112 C_WDIR, /* directory writeble to others, without 113 * sticky bit */ 114 C_NUMCOLORS /* just a place-holder */ 115} Colors; 116 117static const char *defcolors = "exfxcxdxbxegedabagacad"; 118 119/* colors for file types */ 120static struct { 121 int num[2]; 122 int bold; 123} colors[C_NUMCOLORS]; 124#endif 125 126void 127printscol(const DISPLAY *dp) 128{ 129 FTSENT *p; 130 131 for (p = dp->list; p; p = p->fts_link) { 132 if (IS_NOPRINT(p)) 133 continue; 134 (void)printaname(p, dp->s_inode, dp->s_block); 135 (void)putchar('\n'); 136 } 137} 138 139/* 140 * print name in current style 141 */ 142int 143printname(const char *name) 144{ 145 if (f_octal || f_octal_escape) 146 return prn_octal(name); 147 else if (f_nonprint) 148 return prn_printable(name); 149 else 150 return printf("%s", name); 151} 152 153void 154printlong(const DISPLAY *dp) 155{ 156 struct stat *sp; 157 FTSENT *p; 158 NAMES *np; 159 char buf[20]; 160#ifdef COLORLS 161 int color_printed = 0; 162#endif 163 int haveacls; 164 dev_t prevdev; 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 haveacls = 1; 170 prevdev = (dev_t)-1; 171 for (p = dp->list; p; p = p->fts_link) { 172 if (IS_NOPRINT(p)) 173 continue; 174 sp = p->fts_statp; 175 if (f_inode) 176 (void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino); 177 if (f_size) 178 (void)printf("%*jd ", 179 dp->s_block, howmany(sp->st_blocks, blocksize)); 180 strmode(sp->st_mode, buf); 181 /* 182 * Cache whether or not the filesystem supports ACL's to 183 * avoid expensive syscalls. Try again when we change devices. 184 */ 185 if (haveacls || sp->st_dev != prevdev) { 186 aclmode(buf, p, &haveacls); 187 prevdev = sp->st_dev; 188 } 189 np = p->fts_pointer; 190 (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink, 191 sp->st_nlink, dp->s_user, np->user, dp->s_group, 192 np->group); 193 if (f_flags) 194 (void)printf("%-*s ", dp->s_flags, np->flags); 195 if (f_label) 196 (void)printf("%-*s ", dp->s_label, np->label); 197 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 198 if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0) 199 (void)printf("%3d, 0x%08x ", 200 major(sp->st_rdev), 201 (u_int)minor(sp->st_rdev)); 202 else 203 (void)printf("%3d, %3d ", 204 major(sp->st_rdev), minor(sp->st_rdev)); 205 else if (dp->bcfile) 206 (void)printf("%*s%*jd ", 207 8 - dp->s_size, "", dp->s_size, sp->st_size); 208 else 209 printsize(dp->s_size, sp->st_size); 210 if (f_accesstime) 211 printtime(sp->st_atime); 212 else if (f_statustime) 213 printtime(sp->st_ctime); 214 else 215 printtime(sp->st_mtime); 216#ifdef COLORLS 217 if (f_color) 218 color_printed = colortype(sp->st_mode); 219#endif 220 (void)printname(p->fts_name); 221#ifdef COLORLS 222 if (f_color && color_printed) 223 endcolor(0); 224#endif 225 if (f_type) 226 (void)printtype(sp->st_mode); 227 if (S_ISLNK(sp->st_mode)) 228 printlink(p); 229 (void)putchar('\n'); 230 } 231} 232 233void 234printstream(const DISPLAY *dp) 235{ 236 FTSENT *p; 237 int chcnt; 238 239 for (p = dp->list, chcnt = 0; p; p = p->fts_link) { 240 if (p->fts_number == NO_PRINT) 241 continue; 242 if (strlen(p->fts_name) + chcnt + 243 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) { 244 putchar('\n'); 245 chcnt = 0; 246 } 247 chcnt += printaname(p, dp->s_inode, dp->s_block); 248 if (p->fts_link) { 249 printf(", "); 250 chcnt += 2; 251 } 252 } 253 if (chcnt) 254 putchar('\n'); 255} 256 257void 258printcol(const DISPLAY *dp) 259{ 260 static FTSENT **array; 261 static int lastentries = -1; 262 FTSENT *p; 263 FTSENT **narray; 264 int base; 265 int chcnt; 266 int cnt; 267 int col; 268 int colwidth; 269 int endcol; 270 int num; 271 int numcols; 272 int numrows; 273 int row; 274 int tabwidth; 275 276 if (f_notabs) 277 tabwidth = 1; 278 else 279 tabwidth = 8; 280 281 /* 282 * Have to do random access in the linked list -- build a table 283 * of pointers. 284 */ 285 if (dp->entries > lastentries) { 286 if ((narray = 287 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { 288 warn(NULL); 289 printscol(dp); 290 return; 291 } 292 lastentries = dp->entries; 293 array = narray; 294 } 295 for (p = dp->list, num = 0; p; p = p->fts_link) 296 if (p->fts_number != NO_PRINT) 297 array[num++] = p; 298 299 colwidth = dp->maxlen; 300 if (f_inode) 301 colwidth += dp->s_inode + 1; 302 if (f_size) 303 colwidth += dp->s_block + 1; 304 if (f_type) 305 colwidth += 1; 306 307 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); 308 if (termwidth < 2 * colwidth) { 309 printscol(dp); 310 return; 311 } 312 numcols = termwidth / colwidth; 313 numrows = num / numcols; 314 if (num % numcols) 315 ++numrows; 316 317 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) 318 (void)printf("total %lu\n", howmany(dp->btotal, blocksize)); 319 320 base = 0; 321 for (row = 0; row < numrows; ++row) { 322 endcol = colwidth; 323 if (!f_sortacross) 324 base = row; 325 for (col = 0, chcnt = 0; col < numcols; ++col) { 326 chcnt += printaname(array[base], dp->s_inode, 327 dp->s_block); 328 if (f_sortacross) 329 base++; 330 else 331 base += numrows; 332 if (base >= num) 333 break; 334 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) 335 <= endcol) { 336 if (f_sortacross && col + 1 >= numcols) 337 break; 338 (void)putchar(f_notabs ? ' ' : '\t'); 339 chcnt = cnt; 340 } 341 endcol += colwidth; 342 } 343 (void)putchar('\n'); 344 } 345} 346 347/* 348 * print [inode] [size] name 349 * return # of characters printed, no trailing characters. 350 */ 351static int 352printaname(const FTSENT *p, u_long inodefield, u_long sizefield) 353{ 354 struct stat *sp; 355 int chcnt; 356#ifdef COLORLS 357 int color_printed = 0; 358#endif 359 360 sp = p->fts_statp; 361 chcnt = 0; 362 if (f_inode) 363 chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino); 364 if (f_size) 365 chcnt += printf("%*jd ", 366 (int)sizefield, howmany(sp->st_blocks, blocksize)); 367#ifdef COLORLS 368 if (f_color) 369 color_printed = colortype(sp->st_mode); 370#endif 371 chcnt += printname(p->fts_name); 372#ifdef COLORLS 373 if (f_color && color_printed) 374 endcolor(0); 375#endif 376 if (f_type) 377 chcnt += printtype(sp->st_mode); 378 return (chcnt); 379} 380 381static void 382printtime(time_t ftime) 383{ 384 char longstring[80]; 385 static time_t now = 0; 386 const char *format; 387 static int d_first = -1; 388 389 if (d_first < 0) 390 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 391 if (now == 0) 392 now = time(NULL); 393 394#define SIXMONTHS ((365 / 2) * 86400) 395 if (f_sectime) 396 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ 397 format = d_first ? "%e %b %T %Y " : "%b %e %T %Y "; 398 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) 399 /* mmm dd hh:mm || dd mmm hh:mm */ 400 format = d_first ? "%e %b %R " : "%b %e %R "; 401 else 402 /* mmm dd yyyy || dd mmm yyyy */ 403 format = d_first ? "%e %b %Y " : "%b %e %Y "; 404 strftime(longstring, sizeof(longstring), format, localtime(&ftime)); 405 fputs(longstring, stdout); 406} 407 408static int 409printtype(u_int mode) 410{ 411 412 if (f_slash) { 413 if ((mode & S_IFMT) == S_IFDIR) { 414 (void)putchar('/'); 415 return (1); 416 } 417 return (0); 418 } 419 420 switch (mode & S_IFMT) { 421 case S_IFDIR: 422 (void)putchar('/'); 423 return (1); 424 case S_IFIFO: 425 (void)putchar('|'); 426 return (1); 427 case S_IFLNK: 428 (void)putchar('@'); 429 return (1); 430 case S_IFSOCK: 431 (void)putchar('='); 432 return (1); 433 case S_IFWHT: 434 (void)putchar('%'); 435 return (1); 436 default: 437 break; 438 } 439 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 440 (void)putchar('*'); 441 return (1); 442 } 443 return (0); 444} 445 446#ifdef COLORLS 447static int 448putch(int c) 449{ 450 (void)putchar(c); 451 return 0; 452} 453 454static int 455writech(int c) 456{ 457 char tmp = (char)c; 458 459 (void)write(STDOUT_FILENO, &tmp, 1); 460 return 0; 461} 462 463static void 464printcolor(Colors c) 465{ 466 char *ansiseq; 467 468 if (colors[c].bold) 469 tputs(enter_bold, 1, putch); 470 471 if (colors[c].num[0] != -1) { 472 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); 473 if (ansiseq) 474 tputs(ansiseq, 1, putch); 475 } 476 if (colors[c].num[1] != -1) { 477 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); 478 if (ansiseq) 479 tputs(ansiseq, 1, putch); 480 } 481} 482 483static void 484endcolor(int sig) 485{ 486 tputs(ansi_coloff, 1, sig ? writech : putch); 487 tputs(attrs_off, 1, sig ? writech : putch); 488} 489 490static int 491colortype(mode_t mode) 492{ 493 switch (mode & S_IFMT) { 494 case S_IFDIR: 495 if (mode & S_IWOTH) 496 if (mode & S_ISTXT) 497 printcolor(C_WSDIR); 498 else 499 printcolor(C_WDIR); 500 else 501 printcolor(C_DIR); 502 return (1); 503 case S_IFLNK: 504 printcolor(C_LNK); 505 return (1); 506 case S_IFSOCK: 507 printcolor(C_SOCK); 508 return (1); 509 case S_IFIFO: 510 printcolor(C_FIFO); 511 return (1); 512 case S_IFBLK: 513 printcolor(C_BLK); 514 return (1); 515 case S_IFCHR: 516 printcolor(C_CHR); 517 return (1); 518 default:; 519 } 520 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 521 if (mode & S_ISUID) 522 printcolor(C_SUID); 523 else if (mode & S_ISGID) 524 printcolor(C_SGID); 525 else 526 printcolor(C_EXEC); 527 return (1); 528 } 529 return (0); 530} 531 532void 533parsecolors(const char *cs) 534{ 535 int i; 536 int j; 537 size_t len; 538 char c[2]; 539 short legacy_warn = 0; 540 541 if (cs == NULL) 542 cs = ""; /* LSCOLORS not set */ 543 len = strlen(cs); 544 for (i = 0; i < (int)C_NUMCOLORS; i++) { 545 colors[i].bold = 0; 546 547 if (len <= 2 * (size_t)i) { 548 c[0] = defcolors[2 * i]; 549 c[1] = defcolors[2 * i + 1]; 550 } else { 551 c[0] = cs[2 * i]; 552 c[1] = cs[2 * i + 1]; 553 } 554 for (j = 0; j < 2; j++) { 555 /* Legacy colours used 0-7 */ 556 if (c[j] >= '0' && c[j] <= '7') { 557 colors[i].num[j] = c[j] - '0'; 558 if (!legacy_warn) { 559 warnx("LSCOLORS should use " 560 "characters a-h instead of 0-9 (" 561 "see the manual page)"); 562 } 563 legacy_warn = 1; 564 } else if (c[j] >= 'a' && c[j] <= 'h') 565 colors[i].num[j] = c[j] - 'a'; 566 else if (c[j] >= 'A' && c[j] <= 'H') { 567 colors[i].num[j] = c[j] - 'A'; 568 colors[i].bold = 1; 569 } else if (tolower((unsigned char)c[j]) == 'x') 570 colors[i].num[j] = -1; 571 else { 572 warnx("invalid character '%c' in LSCOLORS" 573 " env var", c[j]); 574 colors[i].num[j] = -1; 575 } 576 } 577 } 578} 579 580void 581colorquit(int sig) 582{ 583 endcolor(sig); 584 585 (void)signal(sig, SIG_DFL); 586 (void)kill(getpid(), sig); 587} 588 589#endif /* COLORLS */ 590 591static void 592printlink(const FTSENT *p) 593{ 594 int lnklen; 595 char name[MAXPATHLEN + 1]; 596 char path[MAXPATHLEN + 1]; 597 598 if (p->fts_level == FTS_ROOTLEVEL) 599 (void)snprintf(name, sizeof(name), "%s", p->fts_name); 600 else 601 (void)snprintf(name, sizeof(name), 602 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 603 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 604 (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); 605 return; 606 } 607 path[lnklen] = '\0'; 608 (void)printf(" -> "); 609 (void)printname(path); 610} 611 612static void 613printsize(size_t width, off_t bytes) 614{ 615 double dbytes; 616 unit_t unit; 617 618 if (f_humanval) { 619 dbytes = bytes; 620 unit = unit_adjust(&dbytes); 621 622 if (unit == 0) 623 (void)printf("%*d%c ", 4, (int)bytes, 'B'); 624 else 625 (void)printf("%*.*f%c ", 4, 626 dbytes >= 99.95 ? 0 : 1, dbytes, "BKMGTPE"[unit]); 627 } else 628 (void)printf("%*jd ", (u_int)width, bytes); 629} 630 631/* 632 * Output in "human-readable" format. Uses 3 digits max and puts 633 * unit suffixes at the end. Makes output compact and easy to read, 634 * especially on huge disks. 635 * 636 */ 637static unit_t 638unit_adjust(double *val) 639{ 640 double abval; 641 unit_t unit; 642 u_int unit_sz; 643 644 abval = fabs(*val); 645 646 unit_sz = abval ? (u_int)ilogb(abval) / 10 : 0; 647 648 if (unit_sz >= (u_int)UNIT_MAX) { 649 unit = NONE; 650 } else { 651 unit = unitp[unit_sz]; 652 *val /= (double)vals_base2[unit_sz]; 653 } 654 655 return (unit); 656} 657 658static void 659aclmode(char *buf, const FTSENT *p, int *haveacls) 660{ 661 char name[MAXPATHLEN + 1]; 662 int entries, ret; 663 acl_t facl; 664 acl_entry_t ae; 665 666 /* 667 * Add a + after the standard rwxrwxrwx mode if the file has an 668 * extended ACL. strmode() reserves space at the end of the string. 669 */ 670 if (p->fts_level == FTS_ROOTLEVEL) 671 snprintf(name, sizeof(name), "%s", p->fts_name); 672 else 673 snprintf(name, sizeof(name), "%s/%s", 674 p->fts_parent->fts_accpath, p->fts_name); 675 /* 676 * We have no way to tell whether a symbolic link has an ACL since 677 * pathconf() and acl_get_file() both follow them. 678 */ 679 if (S_ISLNK(p->fts_statp->st_mode)) { 680 *haveacls = 1; 681 return; 682 } 683 if ((ret = pathconf(name, _PC_ACL_EXTENDED)) <= 0) { 684 if (ret < 0 && errno != EINVAL) 685 warn("%s", name); 686 else 687 *haveacls = 0; 688 return; 689 } 690 *haveacls = 1; 691 if ((facl = acl_get_file(name, ACL_TYPE_ACCESS)) != NULL) { 692 if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 1) { 693 entries = 1; 694 while (acl_get_entry(facl, ACL_NEXT_ENTRY, &ae) == 1) 695 if (++entries > 3) 696 break; 697 /* 698 * POSIX.1e requires that ACLs of type ACL_TYPE_ACCESS 699 * must have at least three entries (owner, group, 700 * and other). So anything with more than 3 ACLs looks 701 * interesting to us. 702 */ 703 if (entries > 3) 704 buf[10] = '+'; 705 } 706 acl_free(facl); 707 } else 708 warn("%s", name); 709} 710