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 35 static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; 36 #endif /* not lint */ 37 #endif 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 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 <langinfo.h> 49 #include <libutil.h> 50 #include <stdio.h> 51 #include <stdint.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <time.h> 55 #include <unistd.h> 56 #ifdef COLORLS 57 #include <ctype.h> 58 #include <termcap.h> 59 #include <signal.h> 60 #endif 61 #include <libxo/xo.h> 62 63 #include "ls.h" 64 #include "extern.h" 65 66 static int printaname(const FTSENT *, u_long, u_long); 67 static void printdev(size_t, dev_t); 68 static void printlink(const FTSENT *); 69 static void printtime(const char *, time_t); 70 static int printtype(u_int); 71 static void printsize(const char *, size_t, off_t); 72 #ifdef COLORLS 73 static void endcolor(int); 74 static int colortype(mode_t); 75 #endif 76 static void aclmode(char *, const FTSENT *); 77 78 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 79 80 #ifdef COLORLS 81 /* Most of these are taken from <sys/stat.h> */ 82 typedef enum Colors { 83 C_DIR, /* directory */ 84 C_LNK, /* symbolic link */ 85 C_SOCK, /* socket */ 86 C_FIFO, /* pipe */ 87 C_EXEC, /* executable */ 88 C_BLK, /* block special */ 89 C_CHR, /* character special */ 90 C_SUID, /* setuid executable */ 91 C_SGID, /* setgid executable */ 92 C_WSDIR, /* directory writeble to others, with sticky 93 * bit */ 94 C_WDIR, /* directory writeble to others, without 95 * sticky bit */ 96 C_NUMCOLORS /* just a place-holder */ 97 } Colors; 98 99 static const char *defcolors = "exfxcxdxbxegedabagacad"; 100 101 /* colors for file types */ 102 static struct { 103 int num[2]; 104 int bold; 105 } colors[C_NUMCOLORS]; 106 #endif 107 108 void 109 printscol(const DISPLAY *dp) 110 { 111 FTSENT *p; 112 113 xo_open_list("entry"); 114 for (p = dp->list; p; p = p->fts_link) { 115 if (IS_NOPRINT(p)) 116 continue; 117 xo_open_instance("entry"); 118 (void)printaname(p, dp->s_inode, dp->s_block); 119 xo_close_instance("entry"); 120 xo_emit("\n"); 121 } 122 xo_close_list("entry"); 123 } 124 125 /* 126 * print name in current style 127 */ 128 int 129 printname(const char *field, const char *name) 130 { 131 char fmt[BUFSIZ]; 132 char *s = getname(name); 133 int rc; 134 135 snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field); 136 rc = xo_emit(fmt, s); 137 free(s); 138 return rc; 139 } 140 141 /* 142 * print name in current style 143 */ 144 char * 145 getname(const char *name) 146 { 147 if (f_octal || f_octal_escape) 148 return get_octal(name); 149 else if (f_nonprint) 150 return get_printable(name); 151 else 152 return strdup(name); 153 } 154 155 void 156 printlong(const 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 == NULL || dp->list->fts_level != FTS_ROOTLEVEL) && 167 (f_longform || f_size)) { 168 xo_emit("{L:total} {:total-blocks/%lu}\n", 169 howmany(dp->btotal, blocksize)); 170 } 171 172 xo_open_list("entry"); 173 for (p = dp->list; p; p = p->fts_link) { 174 char *name; 175 if (IS_NOPRINT(p)) 176 continue; 177 xo_open_instance("entry"); 178 sp = p->fts_statp; 179 name = getname(p->fts_name); 180 if (name) 181 xo_emit("{ke:name/%hs}", name); 182 if (f_inode) 183 xo_emit("{:inode/%*ju} ", 184 dp->s_inode, (uintmax_t)sp->st_ino); 185 if (f_size) 186 xo_emit("{:blocks/%*jd} ", 187 dp->s_block, howmany(sp->st_blocks, blocksize)); 188 strmode(sp->st_mode, buf); 189 aclmode(buf, p); 190 np = p->fts_pointer; 191 xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS); 192 xo_emit("{t:mode/%s} {:links/%*u} {:user/%-*s} {:group/%-*s} ", 193 buf, dp->s_nlink, sp->st_nlink, 194 dp->s_user, np->user, dp->s_group, np->group); 195 if (f_flags) 196 xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags); 197 if (f_label) 198 xo_emit("{:label/%-*s} ", dp->s_label, np->label); 199 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 200 printdev(dp->s_size, sp->st_rdev); 201 else 202 printsize("size", dp->s_size, sp->st_size); 203 if (f_accesstime) 204 printtime("access-time", sp->st_atime); 205 else if (f_birthtime) 206 printtime("birth-time", sp->st_birthtime); 207 else if (f_statustime) 208 printtime("change-time", sp->st_ctime); 209 else 210 printtime("modify-time", sp->st_mtime); 211 #ifdef COLORLS 212 if (f_color) 213 color_printed = colortype(sp->st_mode); 214 #endif 215 216 if (name) { 217 xo_emit("{dk:name/%hs}", name); 218 free(name); 219 } 220 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 xo_close_instance("entry"); 230 xo_emit("\n"); 231 } 232 xo_close_list("entry"); 233 } 234 235 void 236 printstream(const DISPLAY *dp) 237 { 238 FTSENT *p; 239 int chcnt; 240 241 for (p = dp->list, chcnt = 0; p; p = p->fts_link) { 242 if (p->fts_number == NO_PRINT) 243 continue; 244 /* XXX strlen does not take octal escapes into account. */ 245 if (strlen(p->fts_name) + chcnt + 246 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) { 247 xo_emit("\n"); 248 chcnt = 0; 249 } 250 chcnt += printaname(p, dp->s_inode, dp->s_block); 251 if (p->fts_link) { 252 xo_emit(", "); 253 chcnt += 2; 254 } 255 } 256 if (chcnt) 257 xo_emit("\n"); 258 } 259 260 void 261 printcol(const DISPLAY *dp) 262 { 263 static FTSENT **array; 264 static int lastentries = -1; 265 FTSENT *p; 266 FTSENT **narray; 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 if ((narray = 290 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { 291 printscol(dp); 292 return; 293 } 294 lastentries = dp->entries; 295 array = narray; 296 } 297 for (p = dp->list, num = 0; p; p = p->fts_link) 298 if (p->fts_number != NO_PRINT) 299 array[num++] = p; 300 301 colwidth = dp->maxlen; 302 if (f_inode) 303 colwidth += dp->s_inode + 1; 304 if (f_size) 305 colwidth += dp->s_block + 1; 306 if (f_type) 307 colwidth += 1; 308 309 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); 310 if (termwidth < 2 * colwidth) { 311 printscol(dp); 312 return; 313 } 314 numcols = termwidth / colwidth; 315 numrows = num / numcols; 316 if (num % numcols) 317 ++numrows; 318 319 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) && 320 (f_longform || f_size)) { 321 xo_emit("{L:total} {:total-blocks/%lu}\n", 322 howmany(dp->btotal, blocksize)); 323 } 324 325 xo_open_list("entry"); 326 base = 0; 327 for (row = 0; row < numrows; ++row) { 328 endcol = colwidth; 329 if (!f_sortacross) 330 base = row; 331 for (col = 0, chcnt = 0; col < numcols; ++col) { 332 xo_open_instance("entry"); 333 chcnt += printaname(array[base], dp->s_inode, 334 dp->s_block); 335 xo_close_instance("entry"); 336 if (f_sortacross) 337 base++; 338 else 339 base += numrows; 340 if (base >= num) 341 break; 342 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) 343 <= endcol) { 344 if (f_sortacross && col + 1 >= numcols) 345 break; 346 xo_emit(f_notabs ? " " : "\t"); 347 chcnt = cnt; 348 } 349 endcol += colwidth; 350 } 351 xo_emit("\n"); 352 } 353 xo_close_list("entry"); 354 } 355 356 /* 357 * print [inode] [size] name 358 * return # of characters printed, no trailing characters. 359 */ 360 static int 361 printaname(const FTSENT *p, u_long inodefield, u_long sizefield) 362 { 363 struct stat *sp; 364 int chcnt; 365 #ifdef COLORLS 366 int color_printed = 0; 367 #endif 368 369 sp = p->fts_statp; 370 chcnt = 0; 371 if (f_inode) 372 chcnt += xo_emit("{:inode/%*ju} ", 373 (int)inodefield, (uintmax_t)sp->st_ino); 374 if (f_size) 375 chcnt += xo_emit("{:size/%*jd} ", 376 (int)sizefield, howmany(sp->st_blocks, blocksize)); 377 #ifdef COLORLS 378 if (f_color) 379 color_printed = colortype(sp->st_mode); 380 #endif 381 chcnt += printname("name", p->fts_name); 382 #ifdef COLORLS 383 if (f_color && color_printed) 384 endcolor(0); 385 #endif 386 if (f_type) 387 chcnt += printtype(sp->st_mode); 388 return (chcnt); 389 } 390 391 /* 392 * Print device special file major and minor numbers. 393 */ 394 static void 395 printdev(size_t width, dev_t dev) 396 { 397 xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev); 398 } 399 400 static void 401 printtime(const char *field, time_t ftime) 402 { 403 char longstring[80]; 404 char fmt[BUFSIZ]; 405 static time_t now = 0; 406 const char *format; 407 static int d_first = -1; 408 409 if (d_first < 0) 410 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 411 if (now == 0) 412 now = time(NULL); 413 414 #define SIXMONTHS ((365 / 2) * 86400) 415 if (f_timeformat) /* user specified format */ 416 format = f_timeformat; 417 else if (f_sectime) 418 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ 419 format = d_first ? "%e %b %T %Y" : "%b %e %T %Y"; 420 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) 421 /* mmm dd hh:mm || dd mmm hh:mm */ 422 format = d_first ? "%e %b %R" : "%b %e %R"; 423 else 424 /* mmm dd yyyy || dd mmm yyyy */ 425 format = d_first ? "%e %b %Y" : "%b %e %Y"; 426 strftime(longstring, sizeof(longstring), format, localtime(&ftime)); 427 428 snprintf(fmt, sizeof(fmt), "{:%s/%%hs} ", field); 429 xo_attr("value", "%ld", (long) ftime); 430 xo_emit(fmt, longstring); 431 } 432 433 static int 434 printtype(u_int mode) 435 { 436 437 if (f_slash) { 438 if ((mode & S_IFMT) == S_IFDIR) { 439 xo_emit("{D:\\/}{e:type/directory}"); 440 return (1); 441 } 442 return (0); 443 } 444 445 switch (mode & S_IFMT) { 446 case S_IFDIR: 447 xo_emit("{D:/\\/}{e:type/directory}"); 448 return (1); 449 case S_IFIFO: 450 xo_emit("{D:|}{e:type/fifo}"); 451 return (1); 452 case S_IFLNK: 453 xo_emit("{D:@}{e:type/link}"); 454 return (1); 455 case S_IFSOCK: 456 xo_emit("{D:=}{e:type/socket}"); 457 return (1); 458 case S_IFWHT: 459 xo_emit("{D:%}{e:type/whiteout}"); 460 return (1); 461 default: 462 break; 463 } 464 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 465 xo_emit("{D:*}{e:executable/}"); 466 return (1); 467 } 468 return (0); 469 } 470 471 #ifdef COLORLS 472 static int 473 putch(int c) 474 { 475 xo_emit("{D:/%c}", c); 476 return 0; 477 } 478 479 static int 480 writech(int c) 481 { 482 char tmp = (char)c; 483 484 (void)write(STDOUT_FILENO, &tmp, 1); 485 return 0; 486 } 487 488 static void 489 printcolor(Colors c) 490 { 491 char *ansiseq; 492 493 if (colors[c].bold) 494 tputs(enter_bold, 1, putch); 495 496 if (colors[c].num[0] != -1) { 497 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); 498 if (ansiseq) 499 tputs(ansiseq, 1, putch); 500 } 501 if (colors[c].num[1] != -1) { 502 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); 503 if (ansiseq) 504 tputs(ansiseq, 1, putch); 505 } 506 } 507 508 static void 509 endcolor(int sig) 510 { 511 tputs(ansi_coloff, 1, sig ? writech : putch); 512 tputs(attrs_off, 1, sig ? writech : putch); 513 } 514 515 static int 516 colortype(mode_t mode) 517 { 518 switch (mode & S_IFMT) { 519 case S_IFDIR: 520 if (mode & S_IWOTH) 521 if (mode & S_ISTXT) 522 printcolor(C_WSDIR); 523 else 524 printcolor(C_WDIR); 525 else 526 printcolor(C_DIR); 527 return (1); 528 case S_IFLNK: 529 printcolor(C_LNK); 530 return (1); 531 case S_IFSOCK: 532 printcolor(C_SOCK); 533 return (1); 534 case S_IFIFO: 535 printcolor(C_FIFO); 536 return (1); 537 case S_IFBLK: 538 printcolor(C_BLK); 539 return (1); 540 case S_IFCHR: 541 printcolor(C_CHR); 542 return (1); 543 default:; 544 } 545 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 546 if (mode & S_ISUID) 547 printcolor(C_SUID); 548 else if (mode & S_ISGID) 549 printcolor(C_SGID); 550 else 551 printcolor(C_EXEC); 552 return (1); 553 } 554 return (0); 555 } 556 557 void 558 parsecolors(const char *cs) 559 { 560 int i; 561 int j; 562 size_t len; 563 char c[2]; 564 short legacy_warn = 0; 565 566 if (cs == NULL) 567 cs = ""; /* LSCOLORS not set */ 568 len = strlen(cs); 569 for (i = 0; i < (int)C_NUMCOLORS; i++) { 570 colors[i].bold = 0; 571 572 if (len <= 2 * (size_t)i) { 573 c[0] = defcolors[2 * i]; 574 c[1] = defcolors[2 * i + 1]; 575 } else { 576 c[0] = cs[2 * i]; 577 c[1] = cs[2 * i + 1]; 578 } 579 for (j = 0; j < 2; j++) { 580 /* Legacy colours used 0-7 */ 581 if (c[j] >= '0' && c[j] <= '7') { 582 colors[i].num[j] = c[j] - '0'; 583 if (!legacy_warn) { 584 xo_warnx("LSCOLORS should use " 585 "characters a-h instead of 0-9 (" 586 "see the manual page)"); 587 } 588 legacy_warn = 1; 589 } else if (c[j] >= 'a' && c[j] <= 'h') 590 colors[i].num[j] = c[j] - 'a'; 591 else if (c[j] >= 'A' && c[j] <= 'H') { 592 colors[i].num[j] = c[j] - 'A'; 593 colors[i].bold = 1; 594 } else if (tolower((unsigned char)c[j]) == 'x') 595 colors[i].num[j] = -1; 596 else { 597 xo_warnx("invalid character '%c' in LSCOLORS" 598 " env var", c[j]); 599 colors[i].num[j] = -1; 600 } 601 } 602 } 603 } 604 605 void 606 colorquit(int sig) 607 { 608 endcolor(sig); 609 610 (void)signal(sig, SIG_DFL); 611 (void)kill(getpid(), sig); 612 } 613 614 #endif /* COLORLS */ 615 616 static void 617 printlink(const FTSENT *p) 618 { 619 int lnklen; 620 char name[MAXPATHLEN + 1]; 621 char path[MAXPATHLEN + 1]; 622 623 if (p->fts_level == FTS_ROOTLEVEL) 624 (void)snprintf(name, sizeof(name), "%s", p->fts_name); 625 else 626 (void)snprintf(name, sizeof(name), 627 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 628 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 629 xo_error("\nls: %s: %s\n", name, strerror(errno)); 630 return; 631 } 632 path[lnklen] = '\0'; 633 xo_emit(" -> "); 634 (void)printname("target", path); 635 } 636 637 static void 638 printsize(const char *field, size_t width, off_t bytes) 639 { 640 char fmt[BUFSIZ]; 641 642 if (f_humanval) { 643 /* 644 * Reserve one space before the size and allocate room for 645 * the trailing '\0'. 646 */ 647 char buf[HUMANVALSTR_LEN - 1 + 1]; 648 649 humanize_number(buf, sizeof(buf), (int64_t)bytes, "", 650 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 651 snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width); 652 xo_attr("value", "%jd", (intmax_t) bytes); 653 xo_emit(fmt, buf); 654 } else { /* with commas */ 655 /* This format assignment needed to work round gcc bug. */ 656 snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ", 657 field, (int) width, f_thousands ? "'" : ""); 658 xo_emit(fmt, (intmax_t) bytes); 659 } 660 } 661 662 /* 663 * Add a + after the standard rwxrwxrwx mode if the file has an 664 * ACL. strmode() reserves space at the end of the string. 665 */ 666 static void 667 aclmode(char *buf, const FTSENT *p) 668 { 669 char name[MAXPATHLEN + 1]; 670 int ret, trivial; 671 static dev_t previous_dev = NODEV; 672 static int supports_acls = -1; 673 static int type = ACL_TYPE_ACCESS; 674 acl_t facl; 675 676 /* 677 * XXX: ACLs are not supported on whiteouts and device files 678 * residing on UFS. 679 */ 680 if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) || 681 S_ISWHT(p->fts_statp->st_mode)) 682 return; 683 684 if (previous_dev == p->fts_statp->st_dev && supports_acls == 0) 685 return; 686 687 if (p->fts_level == FTS_ROOTLEVEL) 688 snprintf(name, sizeof(name), "%s", p->fts_name); 689 else 690 snprintf(name, sizeof(name), "%s/%s", 691 p->fts_parent->fts_accpath, p->fts_name); 692 693 if (previous_dev != p->fts_statp->st_dev) { 694 previous_dev = p->fts_statp->st_dev; 695 supports_acls = 0; 696 697 ret = lpathconf(name, _PC_ACL_NFS4); 698 if (ret > 0) { 699 type = ACL_TYPE_NFS4; 700 supports_acls = 1; 701 } else if (ret < 0 && errno != EINVAL) { 702 xo_warn("%s", name); 703 return; 704 } 705 if (supports_acls == 0) { 706 ret = lpathconf(name, _PC_ACL_EXTENDED); 707 if (ret > 0) { 708 type = ACL_TYPE_ACCESS; 709 supports_acls = 1; 710 } else if (ret < 0 && errno != EINVAL) { 711 xo_warn("%s", name); 712 return; 713 } 714 } 715 } 716 if (supports_acls == 0) 717 return; 718 facl = acl_get_link_np(name, type); 719 if (facl == NULL) { 720 xo_warn("%s", name); 721 return; 722 } 723 if (acl_is_trivial_np(facl, &trivial)) { 724 acl_free(facl); 725 xo_warn("%s", name); 726 return; 727 } 728 if (!trivial) 729 buf[10] = '+'; 730 acl_free(facl); 731 } 732