1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Michael Fischbein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #if 0 36 #ifndef lint 37 static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; 38 #endif /* not lint */ 39 #endif 40 #include <sys/cdefs.h> 41 __FBSDID("$FreeBSD$"); 42 43 #include <sys/param.h> 44 #include <sys/stat.h> 45 #include <sys/acl.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <fts.h> 50 #include <langinfo.h> 51 #include <libutil.h> 52 #include <limits.h> 53 #include <stdio.h> 54 #include <stdint.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <time.h> 58 #include <unistd.h> 59 #include <wchar.h> 60 #ifdef COLORLS 61 #include <ctype.h> 62 #include <termcap.h> 63 #include <signal.h> 64 #endif 65 #include <libxo/xo.h> 66 67 #include "ls.h" 68 #include "extern.h" 69 70 static int printaname(const FTSENT *, u_long, u_long); 71 static void printdev(size_t, dev_t); 72 static void printlink(const FTSENT *); 73 static void printtime(const char *, time_t); 74 static int printtype(u_int); 75 static void printsize(const char *, size_t, off_t); 76 #ifdef COLORLS 77 static void endcolor(int); 78 static int colortype(mode_t); 79 #endif 80 static void aclmode(char *, const FTSENT *); 81 82 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 83 84 #ifdef COLORLS 85 /* Most of these are taken from <sys/stat.h> */ 86 typedef enum Colors { 87 C_DIR, /* directory */ 88 C_LNK, /* symbolic link */ 89 C_SOCK, /* socket */ 90 C_FIFO, /* pipe */ 91 C_EXEC, /* executable */ 92 C_BLK, /* block special */ 93 C_CHR, /* character special */ 94 C_SUID, /* setuid executable */ 95 C_SGID, /* setgid executable */ 96 C_WSDIR, /* directory writeble to others, with sticky 97 * bit */ 98 C_WDIR, /* directory writeble to others, without 99 * sticky bit */ 100 C_NUMCOLORS /* just a place-holder */ 101 } Colors; 102 103 static const char *defcolors = "exfxcxdxbxegedabagacad"; 104 105 /* colors for file types */ 106 static struct { 107 int num[2]; 108 int bold; 109 } colors[C_NUMCOLORS]; 110 #endif 111 112 static size_t padding_for_month[12]; 113 static size_t month_max_size = 0; 114 115 void 116 printscol(const DISPLAY *dp) 117 { 118 FTSENT *p; 119 120 xo_open_list("entry"); 121 for (p = dp->list; p; p = p->fts_link) { 122 if (IS_NOPRINT(p)) 123 continue; 124 xo_open_instance("entry"); 125 (void)printaname(p, dp->s_inode, dp->s_block); 126 xo_close_instance("entry"); 127 xo_emit("\n"); 128 } 129 xo_close_list("entry"); 130 } 131 132 /* 133 * print name in current style 134 */ 135 int 136 printname(const char *field, const char *name) 137 { 138 char fmt[BUFSIZ]; 139 char *s = getname(name); 140 int rc; 141 142 snprintf(fmt, sizeof(fmt), "{:%s/%%hs}", field); 143 rc = xo_emit(fmt, s); 144 free(s); 145 return rc; 146 } 147 148 static const char * 149 get_abmon(int mon) 150 { 151 152 switch (mon) { 153 case 0: return (nl_langinfo(ABMON_1)); 154 case 1: return (nl_langinfo(ABMON_2)); 155 case 2: return (nl_langinfo(ABMON_3)); 156 case 3: return (nl_langinfo(ABMON_4)); 157 case 4: return (nl_langinfo(ABMON_5)); 158 case 5: return (nl_langinfo(ABMON_6)); 159 case 6: return (nl_langinfo(ABMON_7)); 160 case 7: return (nl_langinfo(ABMON_8)); 161 case 8: return (nl_langinfo(ABMON_9)); 162 case 9: return (nl_langinfo(ABMON_10)); 163 case 10: return (nl_langinfo(ABMON_11)); 164 case 11: return (nl_langinfo(ABMON_12)); 165 } 166 167 /* should never happen */ 168 abort(); 169 } 170 171 static size_t 172 mbswidth(const char *month) 173 { 174 wchar_t wc; 175 size_t width, donelen, clen, w; 176 177 width = donelen = 0; 178 while ((clen = mbrtowc(&wc, month + donelen, MB_LEN_MAX, NULL)) != 0) { 179 if (clen == (size_t)-1 || clen == (size_t)-2) 180 return (-1); 181 donelen += clen; 182 if ((w = wcwidth(wc)) == (size_t)-1) 183 return (-1); 184 width += w; 185 } 186 187 return (width); 188 } 189 190 static void 191 compute_abbreviated_month_size(void) 192 { 193 int i; 194 size_t width; 195 size_t months_width[12]; 196 197 for (i = 0; i < 12; i++) { 198 width = mbswidth(get_abmon(i)); 199 if (width == (size_t)-1) { 200 month_max_size = -1; 201 return; 202 } 203 months_width[i] = width; 204 if (width > month_max_size) 205 month_max_size = width; 206 } 207 208 for (i = 0; i < 12; i++) 209 padding_for_month[i] = month_max_size - months_width[i]; 210 } 211 212 /* 213 * print name in current style 214 */ 215 char * 216 getname(const char *name) 217 { 218 if (f_octal || f_octal_escape) 219 return get_octal(name); 220 else if (f_nonprint) 221 return get_printable(name); 222 else 223 return strdup(name); 224 } 225 226 void 227 printlong(const DISPLAY *dp) 228 { 229 struct stat *sp; 230 FTSENT *p; 231 NAMES *np; 232 char buf[20]; 233 #ifdef COLORLS 234 int color_printed = 0; 235 #endif 236 237 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) && 238 (f_longform || f_size)) { 239 xo_emit("{L:total} {:total-blocks/%lu}\n", 240 howmany(dp->btotal, blocksize)); 241 } 242 243 xo_open_list("entry"); 244 for (p = dp->list; p; p = p->fts_link) { 245 char *name, *type; 246 if (IS_NOPRINT(p)) 247 continue; 248 xo_open_instance("entry"); 249 sp = p->fts_statp; 250 name = getname(p->fts_name); 251 if (name) 252 xo_emit("{ke:name/%hs}", name); 253 if (f_inode) 254 xo_emit("{t:inode/%*ju} ", 255 dp->s_inode, (uintmax_t)sp->st_ino); 256 if (f_size) 257 xo_emit("{t:blocks/%*jd} ", 258 dp->s_block, howmany(sp->st_blocks, blocksize)); 259 strmode(sp->st_mode, buf); 260 aclmode(buf, p); 261 np = p->fts_pointer; 262 xo_attr("value", "%03o", (int) sp->st_mode & ALLPERMS); 263 if (f_numericonly) { 264 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*ju} {td:user/%-*s}{e:user/%ju} {td:group/%-*s}{e:group/%ju} ", 265 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)sp->st_nlink, 266 dp->s_user, np->user, (uintmax_t)sp->st_uid, dp->s_group, np->group, (uintmax_t)sp->st_gid); 267 } else { 268 xo_emit("{t:mode/%s}{e:mode_octal/%03o} {t:links/%*ju} {t:user/%-*s} {t:group/%-*s} ", 269 buf, (int) sp->st_mode & ALLPERMS, dp->s_nlink, (uintmax_t)sp->st_nlink, 270 dp->s_user, np->user, dp->s_group, np->group); 271 } 272 if (S_ISBLK(sp->st_mode)) 273 asprintf(&type, "block"); 274 if (S_ISCHR(sp->st_mode)) 275 asprintf(&type, "character"); 276 if (S_ISDIR(sp->st_mode)) 277 asprintf(&type, "directory"); 278 if (S_ISFIFO(sp->st_mode)) 279 asprintf(&type, "fifo"); 280 if (S_ISLNK(sp->st_mode)) 281 asprintf(&type, "symlink"); 282 if (S_ISREG(sp->st_mode)) 283 asprintf(&type, "regular"); 284 if (S_ISSOCK(sp->st_mode)) 285 asprintf(&type, "socket"); 286 if (S_ISWHT(sp->st_mode)) 287 asprintf(&type, "whiteout"); 288 xo_emit("{e:type/%s}", type); 289 free(type); 290 if (f_flags) 291 xo_emit("{:flags/%-*s} ", dp->s_flags, np->flags); 292 if (f_label) 293 xo_emit("{t:label/%-*s} ", dp->s_label, np->label); 294 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 295 printdev(dp->s_size, sp->st_rdev); 296 else 297 printsize("size", dp->s_size, sp->st_size); 298 if (f_accesstime) 299 printtime("access-time", sp->st_atime); 300 else if (f_birthtime) 301 printtime("birth-time", sp->st_birthtime); 302 else if (f_statustime) 303 printtime("change-time", sp->st_ctime); 304 else 305 printtime("modify-time", sp->st_mtime); 306 #ifdef COLORLS 307 if (f_color) 308 color_printed = colortype(sp->st_mode); 309 #endif 310 311 if (name) { 312 xo_emit("{dk:name/%hs}", name); 313 free(name); 314 } 315 316 #ifdef COLORLS 317 if (f_color && color_printed) 318 endcolor(0); 319 #endif 320 if (f_type) 321 (void)printtype(sp->st_mode); 322 if (S_ISLNK(sp->st_mode)) 323 printlink(p); 324 xo_close_instance("entry"); 325 xo_emit("\n"); 326 } 327 xo_close_list("entry"); 328 } 329 330 void 331 printstream(const DISPLAY *dp) 332 { 333 FTSENT *p; 334 int chcnt; 335 336 xo_open_list("entry"); 337 for (p = dp->list, chcnt = 0; p; p = p->fts_link) { 338 if (p->fts_number == NO_PRINT) 339 continue; 340 /* XXX strlen does not take octal escapes into account. */ 341 if (strlen(p->fts_name) + chcnt + 342 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) { 343 xo_emit("\n"); 344 chcnt = 0; 345 } 346 xo_open_instance("file"); 347 chcnt += printaname(p, dp->s_inode, dp->s_block); 348 xo_close_instance("file"); 349 if (p->fts_link) { 350 xo_emit(", "); 351 chcnt += 2; 352 } 353 } 354 xo_close_list("entry"); 355 if (chcnt) 356 xo_emit("\n"); 357 } 358 359 void 360 printcol(const DISPLAY *dp) 361 { 362 static FTSENT **array; 363 static int lastentries = -1; 364 FTSENT *p; 365 FTSENT **narray; 366 int base; 367 int chcnt; 368 int cnt; 369 int col; 370 int colwidth; 371 int endcol; 372 int num; 373 int numcols; 374 int numrows; 375 int row; 376 int tabwidth; 377 378 if (f_notabs) 379 tabwidth = 1; 380 else 381 tabwidth = 8; 382 383 /* 384 * Have to do random access in the linked list -- build a table 385 * of pointers. 386 */ 387 if (dp->entries > lastentries) { 388 if ((narray = 389 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { 390 printscol(dp); 391 return; 392 } 393 lastentries = dp->entries; 394 array = narray; 395 } 396 for (p = dp->list, num = 0; p; p = p->fts_link) 397 if (p->fts_number != NO_PRINT) 398 array[num++] = p; 399 400 colwidth = dp->maxlen; 401 if (f_inode) 402 colwidth += dp->s_inode + 1; 403 if (f_size) 404 colwidth += dp->s_block + 1; 405 if (f_type) 406 colwidth += 1; 407 408 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); 409 if (termwidth < 2 * colwidth) { 410 printscol(dp); 411 return; 412 } 413 numcols = termwidth / colwidth; 414 numrows = num / numcols; 415 if (num % numcols) 416 ++numrows; 417 418 if ((dp->list == NULL || dp->list->fts_level != FTS_ROOTLEVEL) && 419 (f_longform || f_size)) { 420 xo_emit("{L:total} {:total-blocks/%lu}\n", 421 howmany(dp->btotal, blocksize)); 422 } 423 424 xo_open_list("entry"); 425 base = 0; 426 for (row = 0; row < numrows; ++row) { 427 endcol = colwidth; 428 if (!f_sortacross) 429 base = row; 430 for (col = 0, chcnt = 0; col < numcols; ++col) { 431 xo_open_instance("entry"); 432 chcnt += printaname(array[base], dp->s_inode, 433 dp->s_block); 434 xo_close_instance("entry"); 435 if (f_sortacross) 436 base++; 437 else 438 base += numrows; 439 if (base >= num) 440 break; 441 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) 442 <= endcol) { 443 if (f_sortacross && col + 1 >= numcols) 444 break; 445 xo_emit(f_notabs ? " " : "\t"); 446 chcnt = cnt; 447 } 448 endcol += colwidth; 449 } 450 xo_emit("\n"); 451 } 452 xo_close_list("entry"); 453 } 454 455 /* 456 * print [inode] [size] name 457 * return # of characters printed, no trailing characters. 458 */ 459 static int 460 printaname(const FTSENT *p, u_long inodefield, u_long sizefield) 461 { 462 struct stat *sp; 463 int chcnt; 464 #ifdef COLORLS 465 int color_printed = 0; 466 #endif 467 468 sp = p->fts_statp; 469 chcnt = 0; 470 if (f_inode) 471 chcnt += xo_emit("{t:inode/%*ju} ", 472 (int)inodefield, (uintmax_t)sp->st_ino); 473 if (f_size) 474 chcnt += xo_emit("{t:size/%*jd} ", 475 (int)sizefield, howmany(sp->st_blocks, blocksize)); 476 #ifdef COLORLS 477 if (f_color) 478 color_printed = colortype(sp->st_mode); 479 #endif 480 chcnt += printname("name", p->fts_name); 481 #ifdef COLORLS 482 if (f_color && color_printed) 483 endcolor(0); 484 #endif 485 if (f_type) 486 chcnt += printtype(sp->st_mode); 487 return (chcnt); 488 } 489 490 /* 491 * Print device special file major and minor numbers. 492 */ 493 static void 494 printdev(size_t width, dev_t dev) 495 { 496 xo_emit("{:device/%#*jx} ", (u_int)width, (uintmax_t)dev); 497 } 498 499 static size_t 500 ls_strftime(char *str, size_t len, const char *fmt, const struct tm *tm) 501 { 502 char *posb, nfmt[BUFSIZ]; 503 const char *format = fmt; 504 size_t ret; 505 506 if ((posb = strstr(fmt, "%b")) != NULL) { 507 if (month_max_size == 0) { 508 compute_abbreviated_month_size(); 509 } 510 if (month_max_size > 0) { 511 snprintf(nfmt, sizeof(nfmt), "%.*s%s%*s%s", 512 (int)(posb - fmt), fmt, 513 get_abmon(tm->tm_mon), 514 (int)padding_for_month[tm->tm_mon], 515 "", 516 posb + 2); 517 format = nfmt; 518 } 519 } 520 ret = strftime(str, len, format, tm); 521 return (ret); 522 } 523 524 static void 525 printtime(const char *field, time_t ftime) 526 { 527 char longstring[80]; 528 char fmt[BUFSIZ]; 529 static time_t now = 0; 530 const char *format; 531 static int d_first = -1; 532 533 if (d_first < 0) 534 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 535 if (now == 0) 536 now = time(NULL); 537 538 #define SIXMONTHS ((365 / 2) * 86400) 539 if (f_timeformat) /* user specified format */ 540 format = f_timeformat; 541 else if (f_sectime) 542 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ 543 format = d_first ? "%e %b %T %Y" : "%b %e %T %Y"; 544 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) 545 /* mmm dd hh:mm || dd mmm hh:mm */ 546 format = d_first ? "%e %b %R" : "%b %e %R"; 547 else 548 /* mmm dd yyyy || dd mmm yyyy */ 549 format = d_first ? "%e %b %Y" : "%b %e %Y"; 550 ls_strftime(longstring, sizeof(longstring), format, localtime(&ftime)); 551 552 snprintf(fmt, sizeof(fmt), "{d:%s/%%hs} ", field); 553 xo_attr("value", "%ld", (long) ftime); 554 xo_emit(fmt, longstring); 555 snprintf(fmt, sizeof(fmt), "{en:%s/%%ld}", field); 556 xo_emit(fmt, (long) ftime); 557 } 558 559 static int 560 printtype(u_int mode) 561 { 562 563 if (f_slash) { 564 if ((mode & S_IFMT) == S_IFDIR) { 565 xo_emit("{D:\\/}{e:type/directory}"); 566 return (1); 567 } 568 return (0); 569 } 570 571 switch (mode & S_IFMT) { 572 case S_IFDIR: 573 xo_emit("{D:/\\/}{e:type/directory}"); 574 return (1); 575 case S_IFIFO: 576 xo_emit("{D:|}{e:type/fifo}"); 577 return (1); 578 case S_IFLNK: 579 xo_emit("{D:@}{e:type/link}"); 580 return (1); 581 case S_IFSOCK: 582 xo_emit("{D:=}{e:type/socket}"); 583 return (1); 584 case S_IFWHT: 585 xo_emit("{D:%%}{e:type/whiteout}"); 586 return (1); 587 default: 588 break; 589 } 590 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 591 xo_emit("{D:*}{e:executable/}"); 592 return (1); 593 } 594 return (0); 595 } 596 597 #ifdef COLORLS 598 static int 599 putch(int c) 600 { 601 xo_emit("{D:/%c}", c); 602 return 0; 603 } 604 605 static int 606 writech(int c) 607 { 608 char tmp = (char)c; 609 610 (void)write(STDOUT_FILENO, &tmp, 1); 611 return 0; 612 } 613 614 static void 615 printcolor(Colors c) 616 { 617 char *ansiseq; 618 619 if (colors[c].bold) 620 tputs(enter_bold, 1, putch); 621 622 if (colors[c].num[0] != -1) { 623 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); 624 if (ansiseq) 625 tputs(ansiseq, 1, putch); 626 } 627 if (colors[c].num[1] != -1) { 628 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); 629 if (ansiseq) 630 tputs(ansiseq, 1, putch); 631 } 632 } 633 634 static void 635 endcolor(int sig) 636 { 637 tputs(ansi_coloff, 1, sig ? writech : putch); 638 tputs(attrs_off, 1, sig ? writech : putch); 639 } 640 641 static int 642 colortype(mode_t mode) 643 { 644 switch (mode & S_IFMT) { 645 case S_IFDIR: 646 if (mode & S_IWOTH) 647 if (mode & S_ISTXT) 648 printcolor(C_WSDIR); 649 else 650 printcolor(C_WDIR); 651 else 652 printcolor(C_DIR); 653 return (1); 654 case S_IFLNK: 655 printcolor(C_LNK); 656 return (1); 657 case S_IFSOCK: 658 printcolor(C_SOCK); 659 return (1); 660 case S_IFIFO: 661 printcolor(C_FIFO); 662 return (1); 663 case S_IFBLK: 664 printcolor(C_BLK); 665 return (1); 666 case S_IFCHR: 667 printcolor(C_CHR); 668 return (1); 669 default:; 670 } 671 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 672 if (mode & S_ISUID) 673 printcolor(C_SUID); 674 else if (mode & S_ISGID) 675 printcolor(C_SGID); 676 else 677 printcolor(C_EXEC); 678 return (1); 679 } 680 return (0); 681 } 682 683 void 684 parsecolors(const char *cs) 685 { 686 int i; 687 int j; 688 size_t len; 689 char c[2]; 690 short legacy_warn = 0; 691 692 if (cs == NULL) 693 cs = ""; /* LSCOLORS not set */ 694 len = strlen(cs); 695 for (i = 0; i < (int)C_NUMCOLORS; i++) { 696 colors[i].bold = 0; 697 698 if (len <= 2 * (size_t)i) { 699 c[0] = defcolors[2 * i]; 700 c[1] = defcolors[2 * i + 1]; 701 } else { 702 c[0] = cs[2 * i]; 703 c[1] = cs[2 * i + 1]; 704 } 705 for (j = 0; j < 2; j++) { 706 /* Legacy colours used 0-7 */ 707 if (c[j] >= '0' && c[j] <= '7') { 708 colors[i].num[j] = c[j] - '0'; 709 if (!legacy_warn) { 710 xo_warnx("LSCOLORS should use " 711 "characters a-h instead of 0-9 (" 712 "see the manual page)"); 713 } 714 legacy_warn = 1; 715 } else if (c[j] >= 'a' && c[j] <= 'h') 716 colors[i].num[j] = c[j] - 'a'; 717 else if (c[j] >= 'A' && c[j] <= 'H') { 718 colors[i].num[j] = c[j] - 'A'; 719 colors[i].bold = 1; 720 } else if (tolower((unsigned char)c[j]) == 'x') 721 colors[i].num[j] = -1; 722 else { 723 xo_warnx("invalid character '%c' in LSCOLORS" 724 " env var", c[j]); 725 colors[i].num[j] = -1; 726 } 727 } 728 } 729 } 730 731 void 732 colorquit(int sig) 733 { 734 endcolor(sig); 735 736 (void)signal(sig, SIG_DFL); 737 (void)kill(getpid(), sig); 738 } 739 740 #endif /* COLORLS */ 741 742 static void 743 printlink(const FTSENT *p) 744 { 745 int lnklen; 746 char name[MAXPATHLEN + 1]; 747 char path[MAXPATHLEN + 1]; 748 749 if (p->fts_level == FTS_ROOTLEVEL) 750 (void)snprintf(name, sizeof(name), "%s", p->fts_name); 751 else 752 (void)snprintf(name, sizeof(name), 753 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 754 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 755 xo_error("\nls: %s: %s\n", name, strerror(errno)); 756 return; 757 } 758 path[lnklen] = '\0'; 759 xo_emit(" -> "); 760 (void)printname("target", path); 761 } 762 763 static void 764 printsize(const char *field, size_t width, off_t bytes) 765 { 766 char fmt[BUFSIZ]; 767 768 if (f_humanval) { 769 /* 770 * Reserve one space before the size and allocate room for 771 * the trailing '\0'. 772 */ 773 char buf[HUMANVALSTR_LEN - 1 + 1]; 774 775 humanize_number(buf, sizeof(buf), (int64_t)bytes, "", 776 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 777 snprintf(fmt, sizeof(fmt), "{:%s/%%%ds} ", field, (int) width); 778 xo_attr("value", "%jd", (intmax_t) bytes); 779 xo_emit(fmt, buf); 780 } else { /* with commas */ 781 /* This format assignment needed to work round gcc bug. */ 782 snprintf(fmt, sizeof(fmt), "{:%s/%%%dj%sd} ", 783 field, (int) width, f_thousands ? "'" : ""); 784 xo_emit(fmt, (intmax_t) bytes); 785 } 786 } 787 788 /* 789 * Add a + after the standard rwxrwxrwx mode if the file has an 790 * ACL. strmode() reserves space at the end of the string. 791 */ 792 static void 793 aclmode(char *buf, const FTSENT *p) 794 { 795 char name[MAXPATHLEN + 1]; 796 int ret, trivial; 797 static dev_t previous_dev = NODEV; 798 static int supports_acls = -1; 799 static int type = ACL_TYPE_ACCESS; 800 acl_t facl; 801 802 /* 803 * XXX: ACLs are not supported on whiteouts and device files 804 * residing on UFS. 805 */ 806 if (S_ISCHR(p->fts_statp->st_mode) || S_ISBLK(p->fts_statp->st_mode) || 807 S_ISWHT(p->fts_statp->st_mode)) 808 return; 809 810 if (previous_dev == p->fts_statp->st_dev && supports_acls == 0) 811 return; 812 813 if (p->fts_level == FTS_ROOTLEVEL) 814 snprintf(name, sizeof(name), "%s", p->fts_name); 815 else 816 snprintf(name, sizeof(name), "%s/%s", 817 p->fts_parent->fts_accpath, p->fts_name); 818 819 if (previous_dev != p->fts_statp->st_dev) { 820 previous_dev = p->fts_statp->st_dev; 821 supports_acls = 0; 822 823 ret = lpathconf(name, _PC_ACL_NFS4); 824 if (ret > 0) { 825 type = ACL_TYPE_NFS4; 826 supports_acls = 1; 827 } else if (ret < 0 && errno != EINVAL) { 828 xo_warn("%s", name); 829 return; 830 } 831 if (supports_acls == 0) { 832 ret = lpathconf(name, _PC_ACL_EXTENDED); 833 if (ret > 0) { 834 type = ACL_TYPE_ACCESS; 835 supports_acls = 1; 836 } else if (ret < 0 && errno != EINVAL) { 837 xo_warn("%s", name); 838 return; 839 } 840 } 841 } 842 if (supports_acls == 0) 843 return; 844 facl = acl_get_link_np(name, type); 845 if (facl == NULL) { 846 xo_warn("%s", name); 847 return; 848 } 849 if (acl_is_trivial_np(facl, &trivial)) { 850 acl_free(facl); 851 xo_warn("%s", name); 852 return; 853 } 854 if (!trivial) 855 buf[10] = '+'; 856 acl_free(facl); 857 } 858