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