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 #ifndef lint 34 static const char copyright[] = 35 "@(#) Copyright (c) 1989, 1993, 1994\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #if 0 40 #ifndef lint 41 static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; 42 #endif /* not lint */ 43 #endif 44 #include <sys/cdefs.h> 45 __FBSDID("$FreeBSD$"); 46 47 #include <sys/param.h> 48 #include <sys/stat.h> 49 #include <sys/ioctl.h> 50 #include <sys/mac.h> 51 52 #include <dirent.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <fts.h> 56 #include <grp.h> 57 #include <inttypes.h> 58 #include <limits.h> 59 #include <locale.h> 60 #include <pwd.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #ifdef COLORLS 66 #include <termcap.h> 67 #include <signal.h> 68 #endif 69 70 #include "ls.h" 71 #include "extern.h" 72 73 /* 74 * Upward approximation of the maximum number of characters needed to 75 * represent a value of integral type t as a string, excluding the 76 * NUL terminator, with provision for a sign. 77 */ 78 #define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) 79 80 /* 81 * MAKENINES(n) turns n into (10**n)-1. This is useful for converting a width 82 * into a number that wide in decimal. 83 * XXX: Overflows are not considered. 84 */ 85 #define MAKENINES(n) \ 86 do { \ 87 intmax_t i; \ 88 \ 89 /* Use a loop as all values of n are small. */ \ 90 for (i = 1; n > 0; i *= 10) \ 91 n--; \ 92 n = i - 1; \ 93 } while(0) 94 95 static void display(const FTSENT *, FTSENT *, int); 96 static int mastercmp(const FTSENT * const *, const FTSENT * const *); 97 static void traverse(int, char **, int); 98 99 static void (*printfcn)(const DISPLAY *); 100 static int (*sortfcn)(const FTSENT *, const FTSENT *); 101 102 long blocksize; /* block size units */ 103 int termwidth = 80; /* default terminal width */ 104 105 /* flags */ 106 int f_accesstime; /* use time of last access */ 107 int f_birthtime; /* use time of birth */ 108 int f_flags; /* show flags associated with a file */ 109 int f_humanval; /* show human-readable file sizes */ 110 int f_inode; /* print inode */ 111 static int f_kblocks; /* print size in kilobytes */ 112 int f_label; /* show MAC label */ 113 static int f_listdir; /* list actual directory, not contents */ 114 static int f_listdot; /* list files beginning with . */ 115 int f_longform; /* long listing format */ 116 static int f_noautodot; /* do not automatically enable -A for root */ 117 static int f_nofollow; /* don't follow symbolic link arguments */ 118 int f_nonprint; /* show unprintables as ? */ 119 static int f_nosort; /* don't sort output */ 120 int f_notabs; /* don't use tab-separated multi-col output */ 121 static int f_numericonly; /* don't convert uid/gid to name */ 122 int f_octal; /* show unprintables as \xxx */ 123 int f_octal_escape; /* like f_octal but use C escapes if possible */ 124 static int f_recursive; /* ls subdirectories also */ 125 static int f_reversesort; /* reverse whatever sort is used */ 126 int f_samesort; /* sort time and name in same direction */ 127 int f_sectime; /* print full time information */ 128 static int f_singlecol; /* use single column output */ 129 int f_size; /* list size in short listing */ 130 static int f_sizesort; 131 int f_slash; /* similar to f_type, but only for dirs */ 132 int f_sortacross; /* sort across rows, not down columns */ 133 int f_statustime; /* use time of last mode change */ 134 static int f_stream; /* stream the output, separate with commas */ 135 int f_thousands; /* show file sizes with thousands separators */ 136 char *f_timeformat; /* user-specified time format */ 137 static int f_timesort; /* sort by time vice name */ 138 int f_type; /* add type character for non-regular files */ 139 static int f_whiteout; /* show whiteout entries */ 140 141 #ifdef COLORLS 142 int f_color; /* add type in color for non-regular files */ 143 144 char *ansi_bgcol; /* ANSI sequence to set background colour */ 145 char *ansi_fgcol; /* ANSI sequence to set foreground colour */ 146 char *ansi_coloff; /* ANSI sequence to reset colours */ 147 char *attrs_off; /* ANSI sequence to turn off attributes */ 148 char *enter_bold; /* ANSI sequence to set color to bold mode */ 149 #endif 150 151 static int rval; 152 153 int 154 main(int argc, char *argv[]) 155 { 156 static char dot[] = ".", *dotav[] = {dot, NULL}; 157 struct winsize win; 158 int ch, fts_options, notused; 159 char *p; 160 #ifdef COLORLS 161 char termcapbuf[1024]; /* termcap definition buffer */ 162 char tcapbuf[512]; /* capability buffer */ 163 char *bp = tcapbuf; 164 #endif 165 166 (void)setlocale(LC_ALL, ""); 167 168 /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 169 if (isatty(STDOUT_FILENO)) { 170 termwidth = 80; 171 if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 172 termwidth = atoi(p); 173 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 174 win.ws_col > 0) 175 termwidth = win.ws_col; 176 f_nonprint = 1; 177 } else { 178 f_singlecol = 1; 179 /* retrieve environment variable, in case of explicit -C */ 180 p = getenv("COLUMNS"); 181 if (p) 182 termwidth = atoi(p); 183 } 184 185 fts_options = FTS_PHYSICAL; 186 if (getenv("LS_SAMESORT")) 187 f_samesort = 1; 188 while ((ch = getopt(argc, argv, 189 "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) { 190 switch (ch) { 191 /* 192 * The -1, -C, -x and -l options all override each other so 193 * shell aliasing works right. 194 */ 195 case '1': 196 f_singlecol = 1; 197 f_longform = 0; 198 f_stream = 0; 199 break; 200 case 'C': 201 f_sortacross = f_longform = f_singlecol = 0; 202 break; 203 case 'l': 204 f_longform = 1; 205 f_singlecol = 0; 206 f_stream = 0; 207 break; 208 case 'x': 209 f_sortacross = 1; 210 f_longform = 0; 211 f_singlecol = 0; 212 break; 213 /* The -c, -u, and -U options override each other. */ 214 case 'c': 215 f_statustime = 1; 216 f_accesstime = 0; 217 f_birthtime = 0; 218 break; 219 case 'u': 220 f_accesstime = 1; 221 f_statustime = 0; 222 f_birthtime = 0; 223 break; 224 case 'U': 225 f_birthtime = 1; 226 f_accesstime = 0; 227 f_statustime = 0; 228 break; 229 case 'f': 230 f_nosort = 1; 231 /* FALLTHROUGH */ 232 case 'a': 233 fts_options |= FTS_SEEDOT; 234 /* FALLTHROUGH */ 235 case 'A': 236 f_listdot = 1; 237 break; 238 /* The -t and -S options override each other. */ 239 case 'S': 240 f_sizesort = 1; 241 f_timesort = 0; 242 break; 243 case 't': 244 f_timesort = 1; 245 f_sizesort = 0; 246 break; 247 /* Other flags. Please keep alphabetic. */ 248 case ',': 249 f_thousands = 1; 250 break; 251 case 'B': 252 f_nonprint = 0; 253 f_octal = 1; 254 f_octal_escape = 0; 255 break; 256 case 'D': 257 f_timeformat = optarg; 258 break; 259 case 'F': 260 f_type = 1; 261 f_slash = 0; 262 break; 263 case 'G': 264 setenv("CLICOLOR", "", 1); 265 break; 266 case 'H': 267 fts_options |= FTS_COMFOLLOW; 268 f_nofollow = 0; 269 break; 270 case 'I': 271 f_noautodot = 1; 272 break; 273 case 'L': 274 fts_options &= ~FTS_PHYSICAL; 275 fts_options |= FTS_LOGICAL; 276 f_nofollow = 0; 277 break; 278 case 'P': 279 fts_options &= ~FTS_COMFOLLOW; 280 fts_options &= ~FTS_LOGICAL; 281 fts_options |= FTS_PHYSICAL; 282 f_nofollow = 1; 283 break; 284 case 'R': 285 f_recursive = 1; 286 break; 287 case 'T': 288 f_sectime = 1; 289 break; 290 case 'W': 291 f_whiteout = 1; 292 break; 293 case 'Z': 294 f_label = 1; 295 break; 296 case 'b': 297 f_nonprint = 0; 298 f_octal = 0; 299 f_octal_escape = 1; 300 break; 301 /* The -d option turns off the -R option. */ 302 case 'd': 303 f_listdir = 1; 304 f_recursive = 0; 305 break; 306 case 'g': /* Compatibility with 4.3BSD. */ 307 break; 308 case 'h': 309 f_humanval = 1; 310 break; 311 case 'i': 312 f_inode = 1; 313 break; 314 case 'k': 315 f_humanval = 0; 316 f_kblocks = 1; 317 break; 318 case 'm': 319 f_stream = 1; 320 f_singlecol = 0; 321 f_longform = 0; 322 break; 323 case 'n': 324 f_numericonly = 1; 325 break; 326 case 'o': 327 f_flags = 1; 328 break; 329 case 'p': 330 f_slash = 1; 331 f_type = 1; 332 break; 333 case 'q': 334 f_nonprint = 1; 335 f_octal = 0; 336 f_octal_escape = 0; 337 break; 338 case 'r': 339 f_reversesort = 1; 340 break; 341 case 's': 342 f_size = 1; 343 break; 344 case 'w': 345 f_nonprint = 0; 346 f_octal = 0; 347 f_octal_escape = 0; 348 break; 349 case 'y': 350 f_samesort = 1; 351 break; 352 default: 353 case '?': 354 usage(); 355 } 356 } 357 argc -= optind; 358 argv += optind; 359 360 /* Root is -A automatically unless -I. */ 361 if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot) 362 f_listdot = 1; 363 364 /* Enabling of colours is conditional on the environment. */ 365 if (getenv("CLICOLOR") && 366 (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 367 #ifdef COLORLS 368 if (tgetent(termcapbuf, getenv("TERM")) == 1) { 369 ansi_fgcol = tgetstr("AF", &bp); 370 ansi_bgcol = tgetstr("AB", &bp); 371 attrs_off = tgetstr("me", &bp); 372 enter_bold = tgetstr("md", &bp); 373 374 /* To switch colours off use 'op' if 375 * available, otherwise use 'oc', or 376 * don't do colours at all. */ 377 ansi_coloff = tgetstr("op", &bp); 378 if (!ansi_coloff) 379 ansi_coloff = tgetstr("oc", &bp); 380 if (ansi_fgcol && ansi_bgcol && ansi_coloff) 381 f_color = 1; 382 } 383 #else 384 warnx("color support not compiled in"); 385 #endif /*COLORLS*/ 386 387 #ifdef COLORLS 388 if (f_color) { 389 /* 390 * We can't put tabs and color sequences together: 391 * column number will be incremented incorrectly 392 * for "stty oxtabs" mode. 393 */ 394 f_notabs = 1; 395 (void)signal(SIGINT, colorquit); 396 (void)signal(SIGQUIT, colorquit); 397 parsecolors(getenv("LSCOLORS")); 398 } 399 #endif 400 401 /* 402 * If not -F, -i, -l, -s, -S or -t options, don't require stat 403 * information, unless in color mode in which case we do 404 * need this to determine which colors to display. 405 */ 406 if (!f_inode && !f_longform && !f_size && !f_timesort && 407 !f_sizesort && !f_type 408 #ifdef COLORLS 409 && !f_color 410 #endif 411 ) 412 fts_options |= FTS_NOSTAT; 413 414 /* 415 * If not -F, -P, -d or -l options, follow any symbolic links listed on 416 * the command line. 417 */ 418 if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash)) 419 fts_options |= FTS_COMFOLLOW; 420 421 /* 422 * If -W, show whiteout entries 423 */ 424 #ifdef FTS_WHITEOUT 425 if (f_whiteout) 426 fts_options |= FTS_WHITEOUT; 427 #endif 428 429 /* If -i, -l or -s, figure out block size. */ 430 if (f_inode || f_longform || f_size) { 431 if (f_kblocks) 432 blocksize = 2; 433 else { 434 (void)getbsize(¬used, &blocksize); 435 blocksize /= 512; 436 } 437 } 438 /* Select a sort function. */ 439 if (f_reversesort) { 440 if (!f_timesort && !f_sizesort) 441 sortfcn = revnamecmp; 442 else if (f_sizesort) 443 sortfcn = revsizecmp; 444 else if (f_accesstime) 445 sortfcn = revacccmp; 446 else if (f_birthtime) 447 sortfcn = revbirthcmp; 448 else if (f_statustime) 449 sortfcn = revstatcmp; 450 else /* Use modification time. */ 451 sortfcn = revmodcmp; 452 } else { 453 if (!f_timesort && !f_sizesort) 454 sortfcn = namecmp; 455 else if (f_sizesort) 456 sortfcn = sizecmp; 457 else if (f_accesstime) 458 sortfcn = acccmp; 459 else if (f_birthtime) 460 sortfcn = birthcmp; 461 else if (f_statustime) 462 sortfcn = statcmp; 463 else /* Use modification time. */ 464 sortfcn = modcmp; 465 } 466 467 /* Select a print function. */ 468 if (f_singlecol) 469 printfcn = printscol; 470 else if (f_longform) 471 printfcn = printlong; 472 else if (f_stream) 473 printfcn = printstream; 474 else 475 printfcn = printcol; 476 477 if (argc) 478 traverse(argc, argv, fts_options); 479 else 480 traverse(1, dotav, fts_options); 481 exit(rval); 482 } 483 484 static int output; /* If anything output. */ 485 486 /* 487 * Traverse() walks the logical directory structure specified by the argv list 488 * in the order specified by the mastercmp() comparison function. During the 489 * traversal it passes linked lists of structures to display() which represent 490 * a superset (may be exact set) of the files to be displayed. 491 */ 492 static void 493 traverse(int argc, char *argv[], int options) 494 { 495 FTS *ftsp; 496 FTSENT *p, *chp; 497 int ch_options; 498 499 if ((ftsp = 500 fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 501 err(1, "fts_open"); 502 503 /* 504 * We ignore errors from fts_children here since they will be 505 * replicated and signalled on the next call to fts_read() below. 506 */ 507 chp = fts_children(ftsp, 0); 508 if (chp != NULL) 509 display(NULL, chp, options); 510 if (f_listdir) 511 return; 512 513 /* 514 * If not recursing down this tree and don't need stat info, just get 515 * the names. 516 */ 517 ch_options = !f_recursive && !f_label && 518 options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 519 520 while ((p = fts_read(ftsp)) != NULL) 521 switch (p->fts_info) { 522 case FTS_DC: 523 warnx("%s: directory causes a cycle", p->fts_name); 524 break; 525 case FTS_DNR: 526 case FTS_ERR: 527 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 528 rval = 1; 529 break; 530 case FTS_D: 531 if (p->fts_level != FTS_ROOTLEVEL && 532 p->fts_name[0] == '.' && !f_listdot) 533 break; 534 535 /* 536 * If already output something, put out a newline as 537 * a separator. If multiple arguments, precede each 538 * directory with its name. 539 */ 540 if (output) { 541 putchar('\n'); 542 (void)printname(p->fts_path); 543 puts(":"); 544 } else if (argc > 1) { 545 (void)printname(p->fts_path); 546 puts(":"); 547 output = 1; 548 } 549 chp = fts_children(ftsp, ch_options); 550 display(p, chp, options); 551 552 if (!f_recursive && chp != NULL) 553 (void)fts_set(ftsp, p, FTS_SKIP); 554 break; 555 default: 556 break; 557 } 558 if (errno) 559 err(1, "fts_read"); 560 } 561 562 /* 563 * Display() takes a linked list of FTSENT structures and passes the list 564 * along with any other necessary information to the print function. P 565 * points to the parent directory of the display list. 566 */ 567 static void 568 display(const FTSENT *p, FTSENT *list, int options) 569 { 570 struct stat *sp; 571 DISPLAY d; 572 FTSENT *cur; 573 NAMES *np; 574 off_t maxsize; 575 long maxblock; 576 uintmax_t maxinode; 577 u_long btotal, labelstrlen, maxlen, maxnlink; 578 u_long maxlabelstr; 579 u_int sizelen; 580 int maxflags; 581 gid_t maxgroup; 582 uid_t maxuser; 583 size_t flen, ulen, glen; 584 char *initmax; 585 int entries, needstats; 586 const char *user, *group; 587 char *flags, *labelstr = NULL; 588 char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 589 char nuser[STRBUF_SIZEOF(gid_t) + 1]; 590 591 needstats = f_inode || f_longform || f_size; 592 flen = 0; 593 btotal = 0; 594 initmax = getenv("LS_COLWIDTHS"); 595 /* Fields match -lios order. New ones should be added at the end. */ 596 maxlabelstr = maxblock = maxlen = maxnlink = 0; 597 maxuser = maxgroup = maxflags = maxsize = 0; 598 maxinode = 0; 599 if (initmax != NULL && *initmax != '\0') { 600 char *initmax2, *jinitmax; 601 int ninitmax; 602 603 /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 604 jinitmax = malloc(strlen(initmax) * 2 + 2); 605 if (jinitmax == NULL) 606 err(1, "malloc"); 607 initmax2 = jinitmax; 608 if (*initmax == ':') 609 strcpy(initmax2, "0:"), initmax2 += 2; 610 else 611 *initmax2++ = *initmax, *initmax2 = '\0'; 612 for (initmax++; *initmax != '\0'; initmax++) { 613 if (initmax[-1] == ':' && initmax[0] == ':') { 614 *initmax2++ = '0'; 615 *initmax2++ = initmax[0]; 616 initmax2[1] = '\0'; 617 } else { 618 *initmax2++ = initmax[0]; 619 initmax2[1] = '\0'; 620 } 621 } 622 if (initmax2[-1] == ':') 623 strcpy(initmax2, "0"); 624 625 ninitmax = sscanf(jinitmax, 626 " %ju : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", 627 &maxinode, &maxblock, &maxnlink, &maxuser, 628 &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 629 f_notabs = 1; 630 switch (ninitmax) { 631 case 0: 632 maxinode = 0; 633 /* FALLTHROUGH */ 634 case 1: 635 maxblock = 0; 636 /* FALLTHROUGH */ 637 case 2: 638 maxnlink = 0; 639 /* FALLTHROUGH */ 640 case 3: 641 maxuser = 0; 642 /* FALLTHROUGH */ 643 case 4: 644 maxgroup = 0; 645 /* FALLTHROUGH */ 646 case 5: 647 maxflags = 0; 648 /* FALLTHROUGH */ 649 case 6: 650 maxsize = 0; 651 /* FALLTHROUGH */ 652 case 7: 653 maxlen = 0; 654 /* FALLTHROUGH */ 655 case 8: 656 maxlabelstr = 0; 657 /* FALLTHROUGH */ 658 #ifdef COLORLS 659 if (!f_color) 660 #endif 661 f_notabs = 0; 662 /* FALLTHROUGH */ 663 default: 664 break; 665 } 666 MAKENINES(maxinode); 667 MAKENINES(maxblock); 668 MAKENINES(maxnlink); 669 MAKENINES(maxsize); 670 free(jinitmax); 671 } 672 d.s_size = 0; 673 sizelen = 0; 674 flags = NULL; 675 for (cur = list, entries = 0; cur; cur = cur->fts_link) { 676 if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 677 warnx("%s: %s", 678 cur->fts_name, strerror(cur->fts_errno)); 679 cur->fts_number = NO_PRINT; 680 rval = 1; 681 continue; 682 } 683 /* 684 * P is NULL if list is the argv list, to which different rules 685 * apply. 686 */ 687 if (p == NULL) { 688 /* Directories will be displayed later. */ 689 if (cur->fts_info == FTS_D && !f_listdir) { 690 cur->fts_number = NO_PRINT; 691 continue; 692 } 693 } else { 694 /* Only display dot file if -a/-A set. */ 695 if (cur->fts_name[0] == '.' && !f_listdot) { 696 cur->fts_number = NO_PRINT; 697 continue; 698 } 699 } 700 if (cur->fts_namelen > maxlen) 701 maxlen = cur->fts_namelen; 702 if (f_octal || f_octal_escape) { 703 u_long t = len_octal(cur->fts_name, cur->fts_namelen); 704 705 if (t > maxlen) 706 maxlen = t; 707 } 708 if (needstats) { 709 sp = cur->fts_statp; 710 if (sp->st_blocks > maxblock) 711 maxblock = sp->st_blocks; 712 if (sp->st_ino > maxinode) 713 maxinode = sp->st_ino; 714 if (sp->st_nlink > maxnlink) 715 maxnlink = sp->st_nlink; 716 if (sp->st_size > maxsize) 717 maxsize = sp->st_size; 718 719 btotal += sp->st_blocks; 720 if (f_longform) { 721 if (f_numericonly) { 722 (void)snprintf(nuser, sizeof(nuser), 723 "%u", sp->st_uid); 724 (void)snprintf(ngroup, sizeof(ngroup), 725 "%u", sp->st_gid); 726 user = nuser; 727 group = ngroup; 728 } else { 729 user = user_from_uid(sp->st_uid, 0); 730 group = group_from_gid(sp->st_gid, 0); 731 } 732 if ((ulen = strlen(user)) > maxuser) 733 maxuser = ulen; 734 if ((glen = strlen(group)) > maxgroup) 735 maxgroup = glen; 736 if (f_flags) { 737 flags = fflagstostr(sp->st_flags); 738 if (flags != NULL && *flags == '\0') { 739 free(flags); 740 flags = strdup("-"); 741 } 742 if (flags == NULL) 743 err(1, "fflagstostr"); 744 flen = strlen(flags); 745 if (flen > (size_t)maxflags) 746 maxflags = flen; 747 } else 748 flen = 0; 749 labelstr = NULL; 750 if (f_label) { 751 char name[PATH_MAX + 1]; 752 mac_t label; 753 int error; 754 755 error = mac_prepare_file_label(&label); 756 if (error == -1) { 757 warn("MAC label for %s/%s", 758 cur->fts_parent->fts_path, 759 cur->fts_name); 760 goto label_out; 761 } 762 763 if (cur->fts_level == FTS_ROOTLEVEL) 764 snprintf(name, sizeof(name), 765 "%s", cur->fts_name); 766 else 767 snprintf(name, sizeof(name), 768 "%s/%s", cur->fts_parent-> 769 fts_accpath, cur->fts_name); 770 771 if (options & FTS_LOGICAL) 772 error = mac_get_file(name, 773 label); 774 else 775 error = mac_get_link(name, 776 label); 777 if (error == -1) { 778 warn("MAC label for %s/%s", 779 cur->fts_parent->fts_path, 780 cur->fts_name); 781 mac_free(label); 782 goto label_out; 783 } 784 785 error = mac_to_text(label, 786 &labelstr); 787 if (error == -1) { 788 warn("MAC label for %s/%s", 789 cur->fts_parent->fts_path, 790 cur->fts_name); 791 mac_free(label); 792 goto label_out; 793 } 794 mac_free(label); 795 label_out: 796 if (labelstr == NULL) 797 labelstr = strdup("-"); 798 labelstrlen = strlen(labelstr); 799 if (labelstrlen > maxlabelstr) 800 maxlabelstr = labelstrlen; 801 } else 802 labelstrlen = 0; 803 804 if ((np = malloc(sizeof(NAMES) + labelstrlen + 805 ulen + glen + flen + 4)) == NULL) 806 err(1, "malloc"); 807 808 np->user = &np->data[0]; 809 (void)strcpy(np->user, user); 810 np->group = &np->data[ulen + 1]; 811 (void)strcpy(np->group, group); 812 813 if (S_ISCHR(sp->st_mode) || 814 S_ISBLK(sp->st_mode)) { 815 sizelen = snprintf(NULL, 0, 816 "%#jx", (uintmax_t)sp->st_rdev); 817 if (d.s_size < sizelen) 818 d.s_size = sizelen; 819 } 820 821 if (f_flags) { 822 np->flags = &np->data[ulen + glen + 2]; 823 (void)strcpy(np->flags, flags); 824 free(flags); 825 } 826 if (f_label) { 827 np->label = &np->data[ulen + glen + 2 828 + (f_flags ? flen + 1 : 0)]; 829 (void)strcpy(np->label, labelstr); 830 free(labelstr); 831 } 832 cur->fts_pointer = np; 833 } 834 } 835 ++entries; 836 } 837 838 /* 839 * If there are no entries to display, we normally stop right 840 * here. However, we must continue if we have to display the 841 * total block count. In this case, we display the total only 842 * on the second (p != NULL) pass. 843 */ 844 if (!entries && (!(f_longform || f_size) || p == NULL)) 845 return; 846 847 d.list = list; 848 d.entries = entries; 849 d.maxlen = maxlen; 850 if (needstats) { 851 d.btotal = btotal; 852 d.s_block = snprintf(NULL, 0, "%lu", howmany(maxblock, blocksize)); 853 d.s_flags = maxflags; 854 d.s_label = maxlabelstr; 855 d.s_group = maxgroup; 856 d.s_inode = snprintf(NULL, 0, "%ju", maxinode); 857 d.s_nlink = snprintf(NULL, 0, "%lu", maxnlink); 858 sizelen = f_humanval ? HUMANVALSTR_LEN : 859 snprintf(NULL, 0, "%ju", maxsize); 860 if (d.s_size < sizelen) 861 d.s_size = sizelen; 862 d.s_user = maxuser; 863 } 864 if (f_thousands) /* make space for commas */ 865 d.s_size += (d.s_size - 1) / 3; 866 printfcn(&d); 867 output = 1; 868 869 if (f_longform) 870 for (cur = list; cur; cur = cur->fts_link) 871 free(cur->fts_pointer); 872 } 873 874 /* 875 * Ordering for mastercmp: 876 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 877 * as larger than directories. Within either group, use the sort function. 878 * All other levels use the sort function. Error entries remain unsorted. 879 */ 880 static int 881 mastercmp(const FTSENT * const *a, const FTSENT * const *b) 882 { 883 int a_info, b_info; 884 885 a_info = (*a)->fts_info; 886 if (a_info == FTS_ERR) 887 return (0); 888 b_info = (*b)->fts_info; 889 if (b_info == FTS_ERR) 890 return (0); 891 892 if (a_info == FTS_NS || b_info == FTS_NS) 893 return (namecmp(*a, *b)); 894 895 if (a_info != b_info && 896 (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 897 if (a_info == FTS_D) 898 return (1); 899 if (b_info == FTS_D) 900 return (-1); 901 } 902 return (sortfcn(*a, *b)); 903 } 904