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