1*c9828589Ssimonb /* $NetBSD: ls.c,v 1.79 2024/12/11 12:56:31 simonb Exp $ */ 249f0ad86Scgd 361f28255Scgd /* 483ede345Smycroft * Copyright (c) 1989, 1993, 1994 583ede345Smycroft * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * This code is derived from software contributed to Berkeley by 861f28255Scgd * Michael Fischbein. 961f28255Scgd * 1061f28255Scgd * Redistribution and use in source and binary forms, with or without 1161f28255Scgd * modification, are permitted provided that the following conditions 1261f28255Scgd * are met: 1361f28255Scgd * 1. Redistributions of source code must retain the above copyright 1461f28255Scgd * notice, this list of conditions and the following disclaimer. 1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer in the 1761f28255Scgd * documentation and/or other materials provided with the distribution. 18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors 1961f28255Scgd * may be used to endorse or promote products derived from this software 2061f28255Scgd * without specific prior written permission. 2161f28255Scgd * 2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261f28255Scgd * SUCH DAMAGE. 3361f28255Scgd */ 3461f28255Scgd 35b22592e8Schristos #include <sys/cdefs.h> 3661f28255Scgd #ifndef lint 372fe2731dSlukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ 382fe2731dSlukem The Regents of the University of California. All rights reserved."); 3961f28255Scgd #endif /* not lint */ 4061f28255Scgd 4161f28255Scgd #ifndef lint 4249f0ad86Scgd #if 0 4349f0ad86Scgd static char sccsid[] = "@(#)ls.c 8.7 (Berkeley) 8/5/94"; 4449f0ad86Scgd #else 45*c9828589Ssimonb __RCSID("$NetBSD: ls.c,v 1.79 2024/12/11 12:56:31 simonb Exp $"); 4649f0ad86Scgd #endif 4761f28255Scgd #endif /* not lint */ 4861f28255Scgd 491126bbaeSrmind #include <sys/param.h> 50203e4227Smycroft #include <sys/types.h> 5161f28255Scgd #include <sys/stat.h> 5261f28255Scgd #include <sys/ioctl.h> 5383ede345Smycroft 5461f28255Scgd #include <dirent.h> 5583ede345Smycroft #include <err.h> 5683ede345Smycroft #include <errno.h> 57203e4227Smycroft #include <fts.h> 58c87c6d5cStron #include <locale.h> 5983ede345Smycroft #include <stdio.h> 60203e4227Smycroft #include <stdlib.h> 6161f28255Scgd #include <string.h> 6283ede345Smycroft #include <unistd.h> 637f10b1cfSchristos #include <termios.h> 64d91f759eSchristos #include <pwd.h> 65d91f759eSchristos #include <grp.h> 660a97c3f6She #include <util.h> 6783ede345Smycroft 6861f28255Scgd #include "ls.h" 69203e4227Smycroft #include "extern.h" 7061f28255Scgd 71ba2e04dcSlukem static void display(FTSENT *, FTSENT *); 72ba2e04dcSlukem static int mastercmp(const FTSENT **, const FTSENT **); 73ba2e04dcSlukem static void traverse(int, char **, int); 74203e4227Smycroft 75ba2e04dcSlukem static void (*printfcn)(DISPLAY *); 76ba2e04dcSlukem static int (*sortfcn)(const FTSENT *, const FTSENT *); 77203e4227Smycroft 78f6a07377Smycroft #define BY_NAME 0 79f6a07377Smycroft #define BY_SIZE 1 80f6a07377Smycroft #define BY_TIME 2 81f6a07377Smycroft 82203e4227Smycroft long blocksize; /* block size units */ 8361f28255Scgd int termwidth = 80; /* default terminal width */ 84f6a07377Smycroft int sortkey = BY_NAME; 85a4b250cdSsimonb int rval = EXIT_SUCCESS; /* exit value - set if error encountered */ 8661f28255Scgd 8761f28255Scgd /* flags */ 8861f28255Scgd int f_accesstime; /* use time of last access */ 8961f28255Scgd int f_column; /* columnated format */ 90ac591fc0Slukem int f_columnacross; /* columnated format, sorted across */ 91203e4227Smycroft int f_flags; /* show flags associated with a file */ 9275d0e9d0Sgrant int f_grouponly; /* long listing without owner */ 93f4c47802Sgrant int f_humanize; /* humanize the size field */ 944aaf499cSerh int f_commas; /* separate size field with comma */ 9561f28255Scgd int f_inode; /* print inode */ 9661f28255Scgd int f_listdir; /* list actual directory, not contents */ 9761f28255Scgd int f_listdot; /* list files beginning with . */ 9861f28255Scgd int f_longform; /* long listing format */ 9961f28255Scgd int f_nonprint; /* show unprintables as ? */ 10061f28255Scgd int f_nosort; /* don't sort output */ 101ac591fc0Slukem int f_numericonly; /* don't convert uid/gid to name */ 10221ab6335Sjschauma int f_octal; /* print octal escapes for nongraphic characters */ 10321ab6335Sjschauma int f_octal_escape; /* like f_octal but use C escapes if possible */ 10461f28255Scgd int f_recursive; /* ls subdirectories also */ 10561f28255Scgd int f_reversesort; /* reverse whatever sort is used */ 10661f28255Scgd int f_sectime; /* print the real time for all files */ 10761f28255Scgd int f_singlecol; /* use single column output */ 10861f28255Scgd int f_size; /* list size in short listing */ 10961f28255Scgd int f_statustime; /* use time of last mode change */ 110b7443b0fSkleink int f_stream; /* stream format */ 11161f28255Scgd int f_type; /* add type character for non-regular files */ 112b424b8feSkleink int f_typedir; /* add type character for directories */ 113d966913fSmycroft int f_whiteout; /* show whiteout entries */ 1140c47a5c3Schristos int f_fullpath; /* print full pathname, not filename */ 1150c47a5c3Schristos int f_leafonly; /* when recursing, print leaf names only */ 11661f28255Scgd 11744db7ee6Sjoerg __dead static void 11844db7ee6Sjoerg usage(void) 11944db7ee6Sjoerg { 12044db7ee6Sjoerg 12144db7ee6Sjoerg (void)fprintf(stderr, 122c7dda21cSchristos "usage: %s [-1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx] [file ...]\n", 12344db7ee6Sjoerg getprogname()); 12444db7ee6Sjoerg exit(EXIT_FAILURE); 12544db7ee6Sjoerg /* NOTREACHED */ 12644db7ee6Sjoerg } 12744db7ee6Sjoerg 128203e4227Smycroft int 129ba2e04dcSlukem ls_main(int argc, char *argv[]) 13061f28255Scgd { 131203e4227Smycroft static char dot[] = ".", *dotav[] = { dot, NULL }; 13261f28255Scgd struct winsize win; 133ae46649fSsimonb int ch, fts_options; 134203e4227Smycroft int kflag = 0; 1350e2f9ea9Smycroft const char *p; 13661f28255Scgd 137a2ed3bbeShira setprogname(argv[0]); 13878509e7fSchristos (void)setlocale(LC_ALL, ""); 139c87c6d5cStron 140203e4227Smycroft /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 141203e4227Smycroft if (isatty(STDOUT_FILENO)) { 142ff54312fSjschauma if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 143645e4d83Sjtc win.ws_col > 0) 14461f28255Scgd termwidth = win.ws_col; 145203e4227Smycroft f_column = f_nonprint = 1; 14661f28255Scgd } else 14761f28255Scgd f_singlecol = 1; 14861f28255Scgd 149203e4227Smycroft /* Root is -A automatically. */ 15061f28255Scgd if (!getuid()) 15161f28255Scgd f_listdot = 1; 15261f28255Scgd 153203e4227Smycroft fts_options = FTS_PHYSICAL; 154d85eb2bfSmlelstv while ((ch = getopt(argc, argv, "1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx")) 155c7dda21cSchristos != -1) { 15661f28255Scgd switch (ch) { 15761f28255Scgd /* 158b7443b0fSkleink * The -1, -C, -l, -m and -x options all override each other so 159ac591fc0Slukem * shell aliasing works correctly. 16061f28255Scgd */ 16161f28255Scgd case '1': 16261f28255Scgd f_singlecol = 1; 163b7443b0fSkleink f_column = f_columnacross = f_longform = f_stream = 0; 16461f28255Scgd break; 16561f28255Scgd case 'C': 16661f28255Scgd f_column = 1; 167b7443b0fSkleink f_columnacross = f_longform = f_singlecol = f_stream = 168b7443b0fSkleink 0; 16961f28255Scgd break; 17075d0e9d0Sgrant case 'g': 1717c8d837eSkleink if (f_grouponly != -1) 17275d0e9d0Sgrant f_grouponly = 1; 1737c8d837eSkleink f_longform = 1; 1747c8d837eSkleink f_column = f_columnacross = f_singlecol = f_stream = 0; 1757c8d837eSkleink break; 17661f28255Scgd case 'l': 17761f28255Scgd f_longform = 1; 178b7443b0fSkleink f_column = f_columnacross = f_singlecol = f_stream = 0; 1797c8d837eSkleink /* Never let -g take precedence over -l. */ 1807c8d837eSkleink f_grouponly = -1; 181b7443b0fSkleink break; 182b7443b0fSkleink case 'm': 183b7443b0fSkleink f_stream = 1; 184b7443b0fSkleink f_column = f_columnacross = f_longform = f_singlecol = 185b7443b0fSkleink 0; 186ac591fc0Slukem break; 187ac591fc0Slukem case 'x': 188ac591fc0Slukem f_columnacross = 1; 189b7443b0fSkleink f_column = f_longform = f_singlecol = f_stream = 0; 19061f28255Scgd break; 191203e4227Smycroft /* The -c and -u options override each other. */ 19261f28255Scgd case 'c': 19361f28255Scgd f_statustime = 1; 19461f28255Scgd f_accesstime = 0; 19561f28255Scgd break; 19661f28255Scgd case 'u': 19761f28255Scgd f_accesstime = 1; 19861f28255Scgd f_statustime = 0; 19961f28255Scgd break; 20061f28255Scgd case 'F': 20161f28255Scgd f_type = 1; 20261f28255Scgd break; 20361f28255Scgd case 'L': 204203e4227Smycroft fts_options &= ~FTS_PHYSICAL; 205203e4227Smycroft fts_options |= FTS_LOGICAL; 20661f28255Scgd break; 20761f28255Scgd case 'R': 20861f28255Scgd f_recursive = 1; 20961f28255Scgd break; 210ac632886Swiz case 'f': 211ac632886Swiz f_nosort = 1; 212ac632886Swiz /* FALLTHROUGH */ 21361f28255Scgd case 'a': 214203e4227Smycroft fts_options |= FTS_SEEDOT; 21561f28255Scgd /* FALLTHROUGH */ 21661f28255Scgd case 'A': 21761f28255Scgd f_listdot = 1; 21861f28255Scgd break; 219ea274fd6Sjschauma /* The -B option turns off the -b, -q and -w options. */ 22021ab6335Sjschauma case 'B': 2212ccef82cSjschauma f_nonprint = 0; 22221ab6335Sjschauma f_octal = 1; 22321ab6335Sjschauma f_octal_escape = 0; 22421ab6335Sjschauma break; 225ea274fd6Sjschauma /* The -b option turns off the -B, -q and -w options. */ 22621ab6335Sjschauma case 'b': 22721ab6335Sjschauma f_nonprint = 0; 22821ab6335Sjschauma f_octal = 0; 22921ab6335Sjschauma f_octal_escape = 1; 2302ccef82cSjschauma break; 231203e4227Smycroft /* The -d option turns off the -R option. */ 23261f28255Scgd case 'd': 23361f28255Scgd f_listdir = 1; 234203e4227Smycroft f_recursive = 0; 23561f28255Scgd break; 23661f28255Scgd case 'i': 23761f28255Scgd f_inode = 1; 23861f28255Scgd break; 23961f28255Scgd case 'k': 240203e4227Smycroft blocksize = 1024; 241203e4227Smycroft kflag = 1; 242eae5877eSchristos f_humanize = 0; 243203e4227Smycroft break; 244ea274fd6Sjschauma /* The -h option forces all sizes to be measured in bytes. */ 245f4c47802Sgrant case 'h': 246f4c47802Sgrant f_humanize = 1; 247eae5877eSchristos kflag = 0; 2484aaf499cSerh f_commas = 0; 2494aaf499cSerh break; 2504aaf499cSerh case 'M': 2514aaf499cSerh f_humanize = 0; 2524aaf499cSerh f_commas = 1; 253f4c47802Sgrant break; 254ac591fc0Slukem case 'n': 255ac591fc0Slukem f_numericonly = 1; 25641f5d0fdSlukem f_longform = 1; 25741f5d0fdSlukem f_column = f_columnacross = f_singlecol = f_stream = 0; 258ac591fc0Slukem break; 2590c47a5c3Schristos case 'O': 2600c47a5c3Schristos f_leafonly = 1; 2610c47a5c3Schristos break; 262203e4227Smycroft case 'o': 263203e4227Smycroft f_flags = 1; 26461f28255Scgd break; 2650c47a5c3Schristos case 'P': 2660c47a5c3Schristos f_fullpath = 1; 2670c47a5c3Schristos break; 268b424b8feSkleink case 'p': 269b424b8feSkleink f_typedir = 1; 270b424b8feSkleink break; 271ea274fd6Sjschauma /* The -q option turns off the -B, -b and -w options. */ 27261f28255Scgd case 'q': 27361f28255Scgd f_nonprint = 1; 27421ab6335Sjschauma f_octal = 0; 27521ab6335Sjschauma f_octal_escape = 0; 27661f28255Scgd break; 27761f28255Scgd case 'r': 27861f28255Scgd f_reversesort = 1; 27961f28255Scgd break; 280f6a07377Smycroft case 'S': 281f6a07377Smycroft sortkey = BY_SIZE; 282f6a07377Smycroft break; 28361f28255Scgd case 's': 28461f28255Scgd f_size = 1; 28561f28255Scgd break; 28661f28255Scgd case 'T': 28761f28255Scgd f_sectime = 1; 28861f28255Scgd break; 28961f28255Scgd case 't': 290f6a07377Smycroft sortkey = BY_TIME; 29161f28255Scgd break; 292d966913fSmycroft case 'W': 293d966913fSmycroft f_whiteout = 1; 294d966913fSmycroft break; 295ea274fd6Sjschauma /* The -w option turns off the -B, -b and -q options. */ 29621ab6335Sjschauma case 'w': 29721ab6335Sjschauma f_nonprint = 0; 29821ab6335Sjschauma f_octal = 0; 29921ab6335Sjschauma f_octal_escape = 0; 30021ab6335Sjschauma break; 301c7dda21cSchristos case 'X': 302c7dda21cSchristos fts_options |= FTS_XDEV; 303c7dda21cSchristos break; 30461f28255Scgd default: 30561f28255Scgd case '?': 30661f28255Scgd usage(); 30761f28255Scgd } 30861f28255Scgd } 30961f28255Scgd argc -= optind; 31061f28255Scgd argv += optind; 31161f28255Scgd 312ff54312fSjschauma if (f_column || f_columnacross || f_stream) { 313ff54312fSjschauma if ((p = getenv("COLUMNS")) != NULL) 314ff54312fSjschauma termwidth = atoi(p); 315ff54312fSjschauma } 316ff54312fSjschauma 317203e4227Smycroft /* 3187c8d837eSkleink * If both -g and -l options, let -l take precedence. 3197c8d837eSkleink */ 3207c8d837eSkleink if (f_grouponly == -1) 3217c8d837eSkleink f_grouponly = 0; 3227c8d837eSkleink 3237c8d837eSkleink /* 324b424b8feSkleink * If not -F, -i, -l, -p, -S, -s or -t options, don't require stat 325203e4227Smycroft * information. 326203e4227Smycroft */ 327b424b8feSkleink if (!f_inode && !f_longform && !f_size && !f_type && !f_typedir && 328f6a07377Smycroft sortkey == BY_NAME) 329203e4227Smycroft fts_options |= FTS_NOSTAT; 33061f28255Scgd 331203e4227Smycroft /* 332203e4227Smycroft * If not -F, -d or -l options, follow any symbolic links listed on 333203e4227Smycroft * the command line. 334203e4227Smycroft */ 335203e4227Smycroft if (!f_longform && !f_listdir && !f_type) 336203e4227Smycroft fts_options |= FTS_COMFOLLOW; 33761f28255Scgd 338d966913fSmycroft /* 339d943cdadSjtc * If -W, show whiteout entries 340d966913fSmycroft */ 341d966913fSmycroft #ifdef FTS_WHITEOUT 342d966913fSmycroft if (f_whiteout) 343d966913fSmycroft fts_options |= FTS_WHITEOUT; 344d966913fSmycroft #endif 345d966913fSmycroft 3466f08f9dbSabs /* If -i, -l, or -s, figure out block size. */ 3474bce9043Ssimonb if (f_inode || f_longform || f_size) { 348006b4dddScgd if (!kflag) 349ae46649fSsimonb (void)getbsize(NULL, &blocksize); 350*c9828589Ssimonb blocksize /= POSIX_BLOCK_SIZE; 351203e4227Smycroft } 352203e4227Smycroft 353203e4227Smycroft /* Select a sort function. */ 35461f28255Scgd if (f_reversesort) { 355f6a07377Smycroft switch (sortkey) { 356f6a07377Smycroft case BY_NAME: 35761f28255Scgd sortfcn = revnamecmp; 358f6a07377Smycroft break; 359f6a07377Smycroft case BY_SIZE: 360f6a07377Smycroft sortfcn = revsizecmp; 361f6a07377Smycroft break; 362f6a07377Smycroft case BY_TIME: 363f6a07377Smycroft if (f_accesstime) 36461f28255Scgd sortfcn = revacccmp; 36561f28255Scgd else if (f_statustime) 36661f28255Scgd sortfcn = revstatcmp; 36783ede345Smycroft else /* Use modification time. */ 36861f28255Scgd sortfcn = revmodcmp; 369f6a07377Smycroft break; 370f6a07377Smycroft } 37161f28255Scgd } else { 372f6a07377Smycroft switch (sortkey) { 373f6a07377Smycroft case BY_NAME: 37461f28255Scgd sortfcn = namecmp; 375f6a07377Smycroft break; 376f6a07377Smycroft case BY_SIZE: 377f6a07377Smycroft sortfcn = sizecmp; 378f6a07377Smycroft break; 379f6a07377Smycroft case BY_TIME: 380f6a07377Smycroft if (f_accesstime) 38161f28255Scgd sortfcn = acccmp; 38261f28255Scgd else if (f_statustime) 38361f28255Scgd sortfcn = statcmp; 38483ede345Smycroft else /* Use modification time. */ 38561f28255Scgd sortfcn = modcmp; 386f6a07377Smycroft break; 387f6a07377Smycroft } 38861f28255Scgd } 38961f28255Scgd 390203e4227Smycroft /* Select a print function. */ 39161f28255Scgd if (f_singlecol) 39261f28255Scgd printfcn = printscol; 393ac591fc0Slukem else if (f_columnacross) 394ac591fc0Slukem printfcn = printacol; 39561f28255Scgd else if (f_longform) 39661f28255Scgd printfcn = printlong; 397b7443b0fSkleink else if (f_stream) 398b7443b0fSkleink printfcn = printstream; 39961f28255Scgd else 40061f28255Scgd printfcn = printcol; 40161f28255Scgd 402203e4227Smycroft if (argc) 403203e4227Smycroft traverse(argc, argv, fts_options); 404203e4227Smycroft else 405203e4227Smycroft traverse(1, dotav, fts_options); 40678509e7fSchristos return rval; 4079dc385beSmycroft /* NOTREACHED */ 40861f28255Scgd } 40961f28255Scgd 410203e4227Smycroft static int output; /* If anything output. */ 41161f28255Scgd 41261f28255Scgd /* 413203e4227Smycroft * Traverse() walks the logical directory structure specified by the argv list 414203e4227Smycroft * in the order specified by the mastercmp() comparison function. During the 415203e4227Smycroft * traversal it passes linked lists of structures to display() which represent 416203e4227Smycroft * a superset (may be exact set) of the files to be displayed. 41761f28255Scgd */ 418203e4227Smycroft static void 419ba2e04dcSlukem traverse(int argc, char *argv[], int options) 420203e4227Smycroft { 42183ede345Smycroft FTS *ftsp; 42283ede345Smycroft FTSENT *p, *chp; 4237e2f5551Schristos int ch_options, error; 424203e4227Smycroft 425203e4227Smycroft if ((ftsp = 426203e4227Smycroft fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 42785cbf55dSdrochner err(EXIT_FAILURE, NULL); 428203e4227Smycroft 429203e4227Smycroft display(NULL, fts_children(ftsp, 0)); 4307e2f5551Schristos if (f_listdir) { 43178509e7fSchristos (void)fts_close(ftsp); 432203e4227Smycroft return; 4337e2f5551Schristos } 434203e4227Smycroft 435203e4227Smycroft /* 436203e4227Smycroft * If not recursing down this tree and don't need stat info, just get 437203e4227Smycroft * the names. 438203e4227Smycroft */ 439203e4227Smycroft ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 440203e4227Smycroft 44183ede345Smycroft while ((p = fts_read(ftsp)) != NULL) 442203e4227Smycroft switch (p->fts_info) { 443203e4227Smycroft case FTS_DC: 444006b4dddScgd warnx("%s: directory causes a cycle", p->fts_name); 445203e4227Smycroft break; 44683ede345Smycroft case FTS_DNR: 44783ede345Smycroft case FTS_ERR: 448f2413af6Schristos warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 449a4b250cdSsimonb rval = EXIT_FAILURE; 45083ede345Smycroft break; 451203e4227Smycroft case FTS_D: 452203e4227Smycroft if (p->fts_level != FTS_ROOTLEVEL && 4531d895808Schristos p->fts_name[0] == '.' && !f_listdot) { 4541d895808Schristos (void)fts_set(ftsp, p, FTS_SKIP); 455203e4227Smycroft break; 4561d895808Schristos } 457203e4227Smycroft 458203e4227Smycroft /* 459203e4227Smycroft * If already output something, put out a newline as 460203e4227Smycroft * a separator. If multiple arguments, precede each 461203e4227Smycroft * directory with its name. 462203e4227Smycroft */ 4630c47a5c3Schristos if (!f_leafonly) { 464203e4227Smycroft if (output) 465203e4227Smycroft (void)printf("\n%s:\n", p->fts_path); 466203e4227Smycroft else if (argc > 1) { 467203e4227Smycroft (void)printf("%s:\n", p->fts_path); 468203e4227Smycroft output = 1; 469203e4227Smycroft } 4700c47a5c3Schristos } 471203e4227Smycroft 47283ede345Smycroft chp = fts_children(ftsp, ch_options); 47383ede345Smycroft display(p, chp); 474203e4227Smycroft 47583ede345Smycroft if (!f_recursive && chp != NULL) 476203e4227Smycroft (void)fts_set(ftsp, p, FTS_SKIP); 477203e4227Smycroft break; 478203e4227Smycroft } 4797e2f5551Schristos error = errno; 48078509e7fSchristos (void)fts_close(ftsp); 4817e2f5551Schristos errno = error; 48283ede345Smycroft if (errno) 483b7443b0fSkleink err(EXIT_FAILURE, "fts_read"); 484203e4227Smycroft } 485203e4227Smycroft 486203e4227Smycroft /* 487203e4227Smycroft * Display() takes a linked list of FTSENT structures and passes the list 488203e4227Smycroft * along with any other necessary information to the print function. P 489203e4227Smycroft * points to the parent directory of the display list. 490203e4227Smycroft */ 491203e4227Smycroft static void 492ba2e04dcSlukem display(FTSENT *p, FTSENT *list) 493203e4227Smycroft { 494203e4227Smycroft struct stat *sp; 495203e4227Smycroft DISPLAY d; 49683ede345Smycroft FTSENT *cur; 497203e4227Smycroft NAMES *np; 498*c9828589Ssimonb u_int64_t btotal; 499990d25a9Slukem off_t maxsize; 500990d25a9Slukem blkcnt_t maxblock; 50178509e7fSchristos ino_t maxinode; 502990d25a9Slukem int maxmajor, maxminor; 503990d25a9Slukem uint32_t maxnlink; 504990d25a9Slukem int bcfile, entries, flen, glen, ulen, maxflags, maxgroup; 505990d25a9Slukem unsigned int maxlen; 506e30b7628Slukem int maxuser, needstats; 5070e2f9ea9Smycroft const char *user, *group; 508c3a9dd2eSenami char buf[21]; /* 64 bits == 20 digits, +1 for NUL */ 509ac591fc0Slukem char nuser[12], ngroup[12]; 510e30b7628Slukem char *flags = NULL; 511203e4227Smycroft 512203e4227Smycroft /* 513203e4227Smycroft * If list is NULL there are two possibilities: that the parent 514203e4227Smycroft * directory p has no children, or that fts_children() returned an 51583ede345Smycroft * error. We ignore the error case since it will be replicated 51683ede345Smycroft * on the next call to fts_read() on the post-order visit to the 51783ede345Smycroft * directory p, and will be signalled in traverse(). 518203e4227Smycroft */ 51983ede345Smycroft if (list == NULL) 520203e4227Smycroft return; 521203e4227Smycroft 522203e4227Smycroft needstats = f_inode || f_longform || f_size; 523203e4227Smycroft flen = 0; 524e30b7628Slukem maxinode = maxnlink = 0; 525203e4227Smycroft bcfile = 0; 526e30b7628Slukem maxuser = maxgroup = maxflags = maxlen = 0; 527*c9828589Ssimonb btotal = maxblock = maxsize = 0; 5289ded7f63Smycroft maxmajor = maxminor = 0; 529203e4227Smycroft for (cur = list, entries = 0; cur; cur = cur->fts_link) { 530203e4227Smycroft if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 53183ede345Smycroft warnx("%s: %s", 53283ede345Smycroft cur->fts_name, strerror(cur->fts_errno)); 533203e4227Smycroft cur->fts_number = NO_PRINT; 534a4b250cdSsimonb rval = EXIT_FAILURE; 53561f28255Scgd continue; 53661f28255Scgd } 53761f28255Scgd 538203e4227Smycroft /* 539203e4227Smycroft * P is NULL if list is the argv list, to which different rules 540203e4227Smycroft * apply. 541203e4227Smycroft */ 542203e4227Smycroft if (p == NULL) { 543203e4227Smycroft /* Directories will be displayed later. */ 544203e4227Smycroft if (cur->fts_info == FTS_D && !f_listdir) { 545203e4227Smycroft cur->fts_number = NO_PRINT; 546203e4227Smycroft continue; 547203e4227Smycroft } 548203e4227Smycroft } else { 549203e4227Smycroft /* Only display dot file if -a/-A set. */ 550203e4227Smycroft if (cur->fts_name[0] == '.' && !f_listdot) { 551203e4227Smycroft cur->fts_number = NO_PRINT; 552203e4227Smycroft continue; 553203e4227Smycroft } 554203e4227Smycroft } 555203e4227Smycroft if (cur->fts_namelen > maxlen) 556203e4227Smycroft maxlen = cur->fts_namelen; 557203e4227Smycroft if (needstats) { 558203e4227Smycroft sp = cur->fts_statp; 559203e4227Smycroft if (sp->st_blocks > maxblock) 560203e4227Smycroft maxblock = sp->st_blocks; 561203e4227Smycroft if (sp->st_ino > maxinode) 562203e4227Smycroft maxinode = sp->st_ino; 563203e4227Smycroft if (sp->st_nlink > maxnlink) 564203e4227Smycroft maxnlink = sp->st_nlink; 565203e4227Smycroft if (sp->st_size > maxsize) 566203e4227Smycroft maxsize = sp->st_size; 5679ded7f63Smycroft if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) { 5689ded7f63Smycroft bcfile = 1; 5699ded7f63Smycroft if (major(sp->st_rdev) > maxmajor) 5709ded7f63Smycroft maxmajor = major(sp->st_rdev); 5719ded7f63Smycroft if (minor(sp->st_rdev) > maxminor) 5729ded7f63Smycroft maxminor = minor(sp->st_rdev); 5739ded7f63Smycroft } 57461f28255Scgd 575203e4227Smycroft btotal += sp->st_blocks; 576203e4227Smycroft if (f_longform) { 577c3a9dd2eSenami if (f_numericonly || 578c3a9dd2eSenami (user = user_from_uid(sp->st_uid, 0)) == 579c3a9dd2eSenami NULL) { 5801381f684Smycroft (void)snprintf(nuser, sizeof(nuser), 5811381f684Smycroft "%u", sp->st_uid); 582c3a9dd2eSenami user = nuser; 583c3a9dd2eSenami } 584c3a9dd2eSenami if (f_numericonly || 585c3a9dd2eSenami (group = group_from_gid(sp->st_gid, 0)) == 586c3a9dd2eSenami NULL) { 5871381f684Smycroft (void)snprintf(ngroup, sizeof(ngroup), 5881381f684Smycroft "%u", sp->st_gid); 589ac591fc0Slukem group = ngroup; 590ac591fc0Slukem } 591203e4227Smycroft if ((ulen = strlen(user)) > maxuser) 592203e4227Smycroft maxuser = ulen; 593203e4227Smycroft if ((glen = strlen(group)) > maxgroup) 594203e4227Smycroft maxgroup = glen; 595203e4227Smycroft if (f_flags) { 59683ede345Smycroft flags = 59778509e7fSchristos flags_to_string((u_long)sp->st_flags, "-"); 598203e4227Smycroft if ((flen = strlen(flags)) > maxflags) 599203e4227Smycroft maxflags = flen; 600203e4227Smycroft } else 601203e4227Smycroft flen = 0; 60261f28255Scgd 603203e4227Smycroft if ((np = malloc(sizeof(NAMES) + 6048fea6043Selad ulen + glen + flen + 2)) == NULL) 60585cbf55dSdrochner err(EXIT_FAILURE, NULL); 60661f28255Scgd 607203e4227Smycroft np->user = &np->data[0]; 608203e4227Smycroft (void)strcpy(np->user, user); 609203e4227Smycroft np->group = &np->data[ulen + 1]; 610203e4227Smycroft (void)strcpy(np->group, group); 611203e4227Smycroft 612203e4227Smycroft if (f_flags) { 613203e4227Smycroft np->flags = &np->data[ulen + glen + 2]; 614203e4227Smycroft (void)strcpy(np->flags, flags); 6150a97c3f6She free(flags); 616203e4227Smycroft } 617203e4227Smycroft cur->fts_pointer = np; 61861f28255Scgd } 61961f28255Scgd } 620203e4227Smycroft ++entries; 62161f28255Scgd } 622203e4227Smycroft 623203e4227Smycroft if (!entries) 624203e4227Smycroft return; 625203e4227Smycroft 626203e4227Smycroft d.list = list; 627203e4227Smycroft d.entries = entries; 628203e4227Smycroft d.maxlen = maxlen; 629203e4227Smycroft if (needstats) { 630203e4227Smycroft d.btotal = btotal; 631ba2dda9fSsimonb if (f_humanize) { 632ba2dda9fSsimonb d.s_block = 4; /* min buf length for humanize_number */ 633ba2dda9fSsimonb } else { 634067a1826Sdholland (void)snprintf(buf, sizeof(buf), "%lld", 635af723a4dSsommerfeld (long long)howmany(maxblock, blocksize)); 636203e4227Smycroft d.s_block = strlen(buf); 6374aaf499cSerh if (f_commas) /* allow for commas before every third digit */ 6384aaf499cSerh d.s_block += (d.s_block - 1) / 3; 639f4c47802Sgrant } 640203e4227Smycroft d.s_flags = maxflags; 641203e4227Smycroft d.s_group = maxgroup; 64278509e7fSchristos (void)snprintf(buf, sizeof(buf), "%llu", 64378509e7fSchristos (unsigned long long)maxinode); 644203e4227Smycroft d.s_inode = strlen(buf); 645e30b7628Slukem (void)snprintf(buf, sizeof(buf), "%u", maxnlink); 646203e4227Smycroft d.s_nlink = strlen(buf); 647f4c47802Sgrant if (f_humanize) { 648c4d01568Ssimonb d.s_size = 4; /* min buf length for humanize_number */ 649f4c47802Sgrant } else { 650067a1826Sdholland (void)snprintf(buf, sizeof(buf), "%lld", 651c4d01568Ssimonb (long long)maxsize); 652203e4227Smycroft d.s_size = strlen(buf); 6534aaf499cSerh if (f_commas) /* allow for commas before every third digit */ 6544aaf499cSerh d.s_size += (d.s_size - 1) / 3; 655f4c47802Sgrant } 656203e4227Smycroft d.s_user = maxuser; 6579ded7f63Smycroft if (bcfile) { 658067a1826Sdholland (void)snprintf(buf, sizeof(buf), "%d", maxmajor); 6599ded7f63Smycroft d.s_major = strlen(buf); 660067a1826Sdholland (void)snprintf(buf, sizeof(buf), "%d", maxminor); 6619ded7f63Smycroft d.s_minor = strlen(buf); 6629ded7f63Smycroft if (d.s_major + d.s_minor + 2 > d.s_size) 6639ded7f63Smycroft d.s_size = d.s_major + d.s_minor + 2; 6649ded7f63Smycroft else if (d.s_size - d.s_minor - 2 > d.s_major) 6659ded7f63Smycroft d.s_major = d.s_size - d.s_minor - 2; 6669ded7f63Smycroft } else { 6679ded7f63Smycroft d.s_major = 0; 6689ded7f63Smycroft d.s_minor = 0; 6699ded7f63Smycroft } 670203e4227Smycroft } 671203e4227Smycroft 672203e4227Smycroft printfcn(&d); 673203e4227Smycroft output = 1; 674203e4227Smycroft 675203e4227Smycroft if (f_longform) 676203e4227Smycroft for (cur = list; cur; cur = cur->fts_link) 677203e4227Smycroft free(cur->fts_pointer); 678203e4227Smycroft } 679203e4227Smycroft 680203e4227Smycroft /* 681203e4227Smycroft * Ordering for mastercmp: 682203e4227Smycroft * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 683203e4227Smycroft * as larger than directories. Within either group, use the sort function. 684203e4227Smycroft * All other levels use the sort function. Error entries remain unsorted. 685203e4227Smycroft */ 686203e4227Smycroft static int 687ba2e04dcSlukem mastercmp(const FTSENT **a, const FTSENT **b) 688203e4227Smycroft { 68983ede345Smycroft int a_info, b_info; 690203e4227Smycroft 691203e4227Smycroft a_info = (*a)->fts_info; 692203e4227Smycroft if (a_info == FTS_ERR) 693203e4227Smycroft return (0); 694203e4227Smycroft b_info = (*b)->fts_info; 695203e4227Smycroft if (b_info == FTS_ERR) 696203e4227Smycroft return (0); 697203e4227Smycroft 69845e5a869Sthorpej if (a_info == FTS_NS || b_info == FTS_NS) { 699c603307dSmycroft if (b_info != FTS_NS) 700c603307dSmycroft return (1); 701c603307dSmycroft else if (a_info != FTS_NS) 702c603307dSmycroft return (-1); 703c603307dSmycroft else 70400e5d89aSmycroft return (namecmp(*a, *b)); 70545e5a869Sthorpej } 706203e4227Smycroft 707ac591fc0Slukem if (a_info != b_info && !f_listdir && 708ac591fc0Slukem (*a)->fts_level == FTS_ROOTLEVEL) { 709203e4227Smycroft if (a_info == FTS_D) 710203e4227Smycroft return (1); 711203e4227Smycroft else if (b_info == FTS_D) 712203e4227Smycroft return (-1); 713ac591fc0Slukem } 714203e4227Smycroft return (sortfcn(*a, *b)); 715203e4227Smycroft } 716