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}", 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}", 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 xo_warn(NULL); 292 printscol(dp); 293 return; 294 } 295 lastentries = dp->entries; 296 array = narray; 297 } 298 for (p = dp->list, num = 0; p; p = p->fts_link) 299 if (p->fts_number != NO_PRINT) 300 array[num++] = p; 301 302 colwidth = dp->maxlen; 303 if (f_inode) 304 colwidth += dp->s_inode + 1; 305 if (f_size) 306 colwidth += dp->s_block + 1; 307 if (f_type) 308 colwidth += 1; 309 310 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); 311 if (termwidth < 2 * colwidth) { 312 printscol(dp); 313 return; 314 } 315 numcols = termwidth / colwidth; 316 numrows = num / numcols; 317 if (num % numcols) 318 ++numrows; 319 320 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) && 321 (f_longform || f_size)) { 322 xo_emit("{L:total} {:total-blocks/%lu}\n", 323 howmany(dp->btotal, blocksize)); 324 } 325 326 xo_open_list("entry"); 327 base = 0; 328 for (row = 0; row < numrows; ++row) { 329 endcol = colwidth; 330 if (!f_sortacross) 331 base = row; 332 for (col = 0, chcnt = 0; col < numcols; ++col) { 333 xo_open_instance("entry"); 334 chcnt += printaname(array[base], dp->s_inode, 335 dp->s_block); 336 xo_close_instance("entry"); 337 if (f_sortacross) 338 base++; 339 else 340 base += numrows; 341 if (base >= num) 342 break; 343 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) 344 <= endcol) { 345 if (f_sortacross && col + 1 >= numcols) 346 break; 347 xo_emit(f_notabs ? " " : "\t"); 348 chcnt = cnt; 349 } 350 endcol += colwidth; 351 } 352 xo_emit("\n"); 353 } 354 xo_close_list("entry"); 355 } 356 357 /* 358 * print [inode] [size] name 359 * return # of characters printed, no trailing characters. 360 */ 361 static int 362 printaname(const FTSENT *p, u_long inodefield, u_long sizefield) 363 { 364 struct stat *sp; 365 int chcnt; 366 #ifdef COLORLS 367 int color_printed = 0; 368 #endif 369 370 sp = p->fts_statp; 371 chcnt = 0; 372 if (f_inode) 373 chcnt += xo_emit("{:inode/%*ju} ", 374 (int)inodefield, (uintmax_t)sp->st_ino); 375 if (f_size) 376 chcnt += xo_emit("{:size/%*jd} ", 377 (int)sizefield, howmany(sp->st_blocks, blocksize)); 378 #ifdef COLORLS 379 if (f_color) 380 color_printed = colortype(sp->st_mode); 381 #endif 382 chcnt += printname("name", p->fts_name); 383 #ifdef COLORLS 384 if (f_color && color_printed) 385 endcolor(0); 386 #endif 387 if (f_type) 388 chcnt += printtype(sp->st_mode); 389 return (chcnt); 390 } 391 392 /* 393 * Print device special file major and minor numbers. 394 */ 395 static void 396 printdev(size_t width, dev_t dev) 397 { 398 xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev); 399 } 400 401 static void 402 printtime(const char *field, time_t ftime) 403 { 404 char longstring[80]; 405 char fmt[BUFSIZ]; 406 static time_t now = 0; 407 const char *format; 408 static int d_first = -1; 409 410 if (d_first < 0) 411 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 412 if (now == 0) 413 now = time(NULL); 414 415 #define SIXMONTHS ((365 / 2) * 86400) 416 if (f_timeformat) /* user specified format */ 417 format = f_timeformat; 418 else if (f_sectime) 419 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ 420 format = d_first ? "%e %b %T %Y" : "%b %e %T %Y"; 421 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) 422 /* mmm dd hh:mm || dd mmm hh:mm */ 423 format = d_first ? "%e %b %R" : "%b %e %R"; 424 else 425 /* mmm dd yyyy || dd mmm yyyy */ 426 format = d_first ? "%e %b %Y" : "%b %e %Y"; 427 strftime(longstring, sizeof(longstring), format, localtime(&ftime)); 428 429 snprintf(fmt, sizeof(fmt), "{:%s/%%s} ", field); 430 xo_attr("value", "%ld", (long) ftime); 431 xo_emit(fmt, longstring); 432 } 433 434 static int 435 printtype(u_int mode) 436 { 437 438 if (f_slash) { 439 if ((mode & S_IFMT) == S_IFDIR) { 440 xo_emit("{D:\\/}{e:type/directory}"); 441 return (1); 442 } 443 return (0); 444 } 445 446 switch (mode & S_IFMT) { 447 case S_IFDIR: 448 xo_emit("{D:/\\/}{e:type/directory}"); 449 return (1); 450 case S_IFIFO: 451 xo_emit("{D:|}{e:type/fifo}"); 452 return (1); 453 case S_IFLNK: 454 xo_emit("{D:@}{e:type/link}"); 455 return (1); 456 case S_IFSOCK: 457 xo_emit("{D:=}{e:type/socket}"); 458 return (1); 459 case S_IFWHT: 460 xo_emit("{D:%}{e:type/whiteout}"); 461 return (1); 462 default: 463 break; 464 } 465 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 466 xo_emit("{D:*}{e:executable/}"); 467 return (1); 468 } 469 return (0); 470 } 471 472 #ifdef COLORLS 473 static int 474 putch(int c) 475 { 476 xo_emit("{D:/%c}", c); 477 return 0; 478 } 479 480 static int 481 writech(int c) 482 { 483 char tmp = (char)c; 484 485 (void)write(STDOUT_FILENO, &tmp, 1); 486 return 0; 487 } 488 489 static void 490 printcolor(Colors c) 491 { 492 char *ansiseq; 493 494 if (colors[c].bold) 495 tputs(enter_bold, 1, putch); 496 497 if (colors[c].num[0] != -1) { 498 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); 499 if (ansiseq) 500 tputs(ansiseq, 1, putch); 501 } 502 if (colors[c].num[1] != -1) { 503 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); 504 if (ansiseq) 505 tputs(ansiseq, 1, putch); 506 } 507 } 508 509 static void 510 endcolor(int sig) 511 { 512 tputs(ansi_coloff, 1, sig ? writech : putch); 513 tputs(attrs_off, 1, sig ? writech : putch); 514 } 515 516 static int 517 colortype(mode_t mode) 518 { 519 switch (mode & S_IFMT) { 520 case S_IFDIR: 521 if (mode & S_IWOTH) 522 if (mode & S_ISTXT) 523 printcolor(C_WSDIR); 524 else 525 printcolor(C_WDIR); 526 else 527 printcolor(C_DIR); 528 return (1); 529 case S_IFLNK: 530 printcolor(C_LNK); 531 return (1); 532 case S_IFSOCK: 533 printcolor(C_SOCK); 534 return (1); 535 case S_IFIFO: 536 printcolor(C_FIFO); 537 return (1); 538 case S_IFBLK: 539 printcolor(C_BLK); 540 return (1); 541 case S_IFCHR: 542 printcolor(C_CHR); 543 return (1); 544 default:; 545 } 546 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 547 if (mode & S_ISUID) 548 printcolor(C_SUID); 549 else if (mode & S_ISGID) 550 printcolor(C_SGID); 551 else 552 printcolor(C_EXEC); 553 return (1); 554 } 555 return (0); 556 } 557 558 void 559 parsecolors(const char *cs) 560 { 561 int i; 562 int j; 563 size_t len; 564 char c[2]; 565 short legacy_warn = 0; 566 567 if (cs == NULL) 568 cs = ""; /* LSCOLORS not set */ 569 len = strlen(cs); 570 for (i = 0; i < (int)C_NUMCOLORS; i++) { 571 colors[i].bold = 0; 572 573 if (len <= 2 * (size_t)i) { 574 c[0] = defcolors[2 * i]; 575 c[1] = defcolors[2 * i + 1]; 576 } else { 577 c[0] = cs[2 * i]; 578 c[1] = cs[2 * i + 1]; 579 } 580 for (j = 0; j < 2; j++) { 581 /* Legacy colours used 0-7 */ 582 if (c[j] >= '0' && c[j] <= '7') { 583 colors[i].num[j] = c[j] - '0'; 584 if (!legacy_warn) { 585 xo_warnx("LSCOLORS should use " 586 "characters a-h instead of 0-9 (" 587 "see the manual page)"); 588 } 589 legacy_warn = 1; 590 } else if (c[j] >= 'a' && c[j] <= 'h') 591 colors[i].num[j] = c[j] - 'a'; 592 else if (c[j] >= 'A' && c[j] <= 'H') { 593 colors[i].num[j] = c[j] - 'A'; 594 colors[i].bold = 1; 595 } else if (tolower((unsigned char)c[j]) == 'x') 596 colors[i].num[j] = -1; 597 else { 598 xo_warnx("invalid character '%c' in LSCOLORS" 599 " env var", c[j]); 600 colors[i].num[j] = -1; 601 } 602 } 603 } 604 } 605 606 void 607 colorquit(int sig) 608 { 609 endcolor(sig); 610 611 (void)signal(sig, SIG_DFL); 612 (void)kill(getpid(), sig); 613 } 614 615 #endif /* COLORLS */ 616 617 static void 618 printlink(const FTSENT *p) 619 { 620 int lnklen; 621 char name[MAXPATHLEN + 1]; 622 char path[MAXPATHLEN + 1]; 623 624 if (p->fts_level == FTS_ROOTLEVEL) 625 (void)snprintf(name, sizeof(name), "%s", p->fts_name); 626 else 627 (void)snprintf(name, sizeof(name), 628 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 629 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 630 xo_error("\nls: %s: %s\n", name, strerror(errno)); 631 return; 632 } 633 path[lnklen] = '\0'; 634 xo_emit(" -> "); 635 (void)printname("target", path); 636 } 637 638 static void 639 printsize(const char *field, size_t width, off_t bytes) 640 { 641 char fmt[BUFSIZ]; 642 643 if (f_humanval) { 644 /* 645 * Reserve one space before the size and allocate room for 646 * the trailing '\0'. 647 */ 648 char buf[HUMANVALSTR_LEN - 1 + 1]; 649 650 humanize_number(buf, sizeof(buf), (int64_t)bytes, "", 651 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 652 snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width); 653 xo_attr("value", "%jd", (intmax_t) bytes); 654 xo_emit(fmt, buf); 655 } else { /* with commas */ 656 /* This format assignment needed to work round gcc bug. */ 657 snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ", 658 field, (int) width, f_thousands ? "'" : ""); 659 xo_emit(fmt, (intmax_t) bytes); 660 } 661 } 662 663 /* 664 * Add a + after the standard rwxrwxrwx mode if the file has an 665 * ACL. strmode() reserves space at the end of the string. 666 */ 667 static void 668 aclmode(char *buf, const FTSENT *p) 669 { 670 char name[MAXPATHLEN + 1]; 671 int ret, trivial; 672 static dev_t previous_dev = NODEV; 673 static int supports_acls = -1; 674 static int type = ACL_TYPE_ACCESS; 675 acl_t facl; 676 677 /* 678 * XXX: ACLs are not supported on whiteouts and device files 679 * residing on UFS. 680 */ 681 if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) || 682 S_ISWHT(p->fts_statp->st_mode)) 683 return; 684 685 if (previous_dev == p->fts_statp->st_dev && supports_acls == 0) 686 return; 687 688 if (p->fts_level == FTS_ROOTLEVEL) 689 snprintf(name, sizeof(name), "%s", p->fts_name); 690 else 691 snprintf(name, sizeof(name), "%s/%s", 692 p->fts_parent->fts_accpath, p->fts_name); 693 694 if (previous_dev != p->fts_statp->st_dev) { 695 previous_dev = p->fts_statp->st_dev; 696 supports_acls = 0; 697 698 ret = lpathconf(name, _PC_ACL_NFS4); 699 if (ret > 0) { 700 type = ACL_TYPE_NFS4; 701 supports_acls = 1; 702 } else if (ret < 0 && errno != EINVAL) { 703 xo_warn("%s", name); 704 return; 705 } 706 if (supports_acls == 0) { 707 ret = lpathconf(name, _PC_ACL_EXTENDED); 708 if (ret > 0) { 709 type = ACL_TYPE_ACCESS; 710 supports_acls = 1; 711 } else if (ret < 0 && errno != EINVAL) { 712 xo_warn("%s", name); 713 return; 714 } 715 } 716 } 717 if (supports_acls == 0) 718 return; 719 facl = acl_get_link_np(name, type); 720 if (facl == NULL) { 721 xo_warn("%s", name); 722 return; 723 } 724 if (acl_is_trivial_np(facl, &trivial)) { 725 acl_free(facl); 726 xo_warn("%s", name); 727 return; 728 } 729 if (!trivial) 730 buf[10] = '+'; 731 acl_free(facl); 732 } 733