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