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