1139969Simp/*- 21556Srgrimes * Copyright (c) 1989, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Michael Fischbein. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 4. Neither the name of the University nor the names of its contributors 171556Srgrimes * may be used to endorse or promote products derived from this software 181556Srgrimes * without specific prior written permission. 191556Srgrimes * 201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301556Srgrimes * SUCH DAMAGE. 311556Srgrimes */ 321556Srgrimes 331556Srgrimes#ifndef lint 3427958Sstevestatic const char copyright[] = 351556Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 361556Srgrimes The Regents of the University of California. All rights reserved.\n"; 3727967Ssteve#endif /* not lint */ 3827967Ssteve 3990153Smarkm#if 0 4027967Ssteve#ifndef lint 4127967Sstevestatic char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; 4290153Smarkm#endif /* not lint */ 4327967Ssteve#endif 4499109Sobrien#include <sys/cdefs.h> 4599109Sobrien__FBSDID("$FreeBSD$"); 461556Srgrimes 47226509Sdes#include <sys/param.h> 481556Srgrimes#include <sys/stat.h> 491556Srgrimes#include <sys/ioctl.h> 50105832Srwatson#include <sys/mac.h> 511556Srgrimes 521556Srgrimes#include <dirent.h> 531556Srgrimes#include <err.h> 541556Srgrimes#include <errno.h> 551556Srgrimes#include <fts.h> 5690878Simp#include <grp.h> 57114583Smarkm#include <inttypes.h> 5850050Ssheldonh#include <limits.h> 5950050Ssheldonh#include <locale.h> 6090878Simp#include <pwd.h> 611556Srgrimes#include <stdio.h> 621556Srgrimes#include <stdlib.h> 631556Srgrimes#include <string.h> 6461289Sache#include <unistd.h> 6561268Sjoe#ifdef COLORLS 6661289Sache#include <termcap.h> 6761289Sache#include <signal.h> 6861268Sjoe#endif 691556Srgrimes 701556Srgrimes#include "ls.h" 711556Srgrimes#include "extern.h" 721556Srgrimes 7350050Ssheldonh/* 7450050Ssheldonh * Upward approximation of the maximum number of characters needed to 7550050Ssheldonh * represent a value of integral type t as a string, excluding the 7650050Ssheldonh * NUL terminator, with provision for a sign. 7750050Ssheldonh */ 7850051Ssheldonh#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) 7950050Ssheldonh 80114583Smarkm/* 81242840Speter * MAKENINES(n) turns n into (10**n)-1. This is useful for converting a width 82114583Smarkm * into a number that wide in decimal. 83114583Smarkm * XXX: Overflows are not considered. 84114583Smarkm */ 85114583Smarkm#define MAKENINES(n) \ 86114583Smarkm do { \ 87114583Smarkm intmax_t i; \ 88114583Smarkm \ 89114583Smarkm /* Use a loop as all values of n are small. */ \ 90114583Smarkm for (i = 1; n > 0; i *= 10) \ 91114583Smarkm n--; \ 92114583Smarkm n = i - 1; \ 93114583Smarkm } while(0) 94114583Smarkm 95114583Smarkmstatic void display(const FTSENT *, FTSENT *, int); 96103726Swollmanstatic int mastercmp(const FTSENT * const *, const FTSENT * const *); 9790110Simpstatic void traverse(int, char **, int); 981556Srgrimes 99114583Smarkmstatic void (*printfcn)(const DISPLAY *); 10090110Simpstatic int (*sortfcn)(const FTSENT *, const FTSENT *); 1011556Srgrimes 1021556Srgrimeslong blocksize; /* block size units */ 1031556Srgrimesint termwidth = 80; /* default terminal width */ 1041556Srgrimes 1051556Srgrimes/* flags */ 10690150Smarkm int f_accesstime; /* use time of last access */ 107157098Sjhb int f_birthtime; /* use time of birth */ 10890150Smarkm int f_flags; /* show flags associated with a file */ 10990150Smarkm int f_humanval; /* show human-readable file sizes */ 11090150Smarkm int f_inode; /* print inode */ 11190150Smarkmstatic int f_kblocks; /* print size in kilobytes */ 112242725Sgrog int f_label; /* show MAC label */ 11390150Smarkmstatic int f_listdir; /* list actual directory, not contents */ 11490150Smarkmstatic int f_listdot; /* list files beginning with . */ 115242725Sgrog int f_longform; /* long listing format */ 116152469Srustatic int f_noautodot; /* do not automatically enable -A for root */ 117203665Sjhstatic int f_nofollow; /* don't follow symbolic link arguments */ 11890150Smarkm int f_nonprint; /* show unprintables as ? */ 11990150Smarkmstatic int f_nosort; /* don't sort output */ 12090150Smarkm int f_notabs; /* don't use tab-separated multi-col output */ 12190150Smarkmstatic int f_numericonly; /* don't convert uid/gid to name */ 12290150Smarkm int f_octal; /* show unprintables as \xxx */ 12390150Smarkm int f_octal_escape; /* like f_octal but use C escapes if possible */ 12490150Smarkmstatic int f_recursive; /* ls subdirectories also */ 12590150Smarkmstatic int f_reversesort; /* reverse whatever sort is used */ 126242725Sgrog int f_samesort; /* sort time and name in same direction */ 127242725Sgrog int f_sectime; /* print full time information */ 12890150Smarkmstatic int f_singlecol; /* use single column output */ 12990150Smarkm int f_size; /* list size in short listing */ 130242725Sgrogstatic int f_sizesort; 13196892Stjr int f_slash; /* similar to f_type, but only for dirs */ 132177907Sgrog int f_sortacross; /* sort across rows, not down columns */ 13390150Smarkm int f_statustime; /* use time of last mode change */ 134114583Smarkmstatic int f_stream; /* stream the output, separate with commas */ 135242725Sgrog int f_thousands; /* show file sizes with thousands separators */ 136242807Sgrog char *f_timeformat; /* user-specified time format */ 13790150Smarkmstatic int f_timesort; /* sort by time vice name */ 13890150Smarkm int f_type; /* add type character for non-regular files */ 13990150Smarkmstatic int f_whiteout; /* show whiteout entries */ 140242725Sgrog 14161268Sjoe#ifdef COLORLS 14290150Smarkm int f_color; /* add type in color for non-regular files */ 14361271Sjoe 14461271Sjoechar *ansi_bgcol; /* ANSI sequence to set background colour */ 14561271Sjoechar *ansi_fgcol; /* ANSI sequence to set foreground colour */ 14661271Sjoechar *ansi_coloff; /* ANSI sequence to reset colours */ 14788583Sjoechar *attrs_off; /* ANSI sequence to turn off attributes */ 14888583Sjoechar *enter_bold; /* ANSI sequence to set color to bold mode */ 14961268Sjoe#endif 1501556Srgrimes 15190150Smarkmstatic int rval; 15217852Sadam 1531556Srgrimesint 15490110Simpmain(int argc, char *argv[]) 1551556Srgrimes{ 15688602Sjoe static char dot[] = ".", *dotav[] = {dot, NULL}; 1571556Srgrimes struct winsize win; 1581556Srgrimes int ch, fts_options, notused; 1591556Srgrimes char *p; 16061271Sjoe#ifdef COLORLS 16188602Sjoe char termcapbuf[1024]; /* termcap definition buffer */ 16288602Sjoe char tcapbuf[512]; /* capability buffer */ 16361271Sjoe char *bp = tcapbuf; 16461271Sjoe#endif 16561271Sjoe 16688602Sjoe (void)setlocale(LC_ALL, ""); 16711808Sache 1681556Srgrimes /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 1691556Srgrimes if (isatty(STDOUT_FILENO)) { 17097803Stjr termwidth = 80; 17197803Stjr if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 17297803Stjr termwidth = atoi(p); 17397803Stjr else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 17497803Stjr win.ws_col > 0) 1751556Srgrimes termwidth = win.ws_col; 17690150Smarkm f_nonprint = 1; 1775158Sjoerg } else { 1781556Srgrimes f_singlecol = 1; 1795158Sjoerg /* retrieve environment variable, in case of explicit -C */ 18090150Smarkm p = getenv("COLUMNS"); 18190150Smarkm if (p) 1825158Sjoerg termwidth = atoi(p); 1835158Sjoerg } 1841556Srgrimes 1851556Srgrimes fts_options = FTS_PHYSICAL; 186242725Sgrog if (getenv("LS_SAMESORT")) 187242725Sgrog f_samesort = 1; 188242807Sgrog while ((ch = getopt(argc, argv, 189242725Sgrog "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) { 1901556Srgrimes switch (ch) { 1911556Srgrimes /* 19296892Stjr * The -1, -C, -x and -l options all override each other so 19396892Stjr * shell aliasing works right. 1941556Srgrimes */ 1951556Srgrimes case '1': 1961556Srgrimes f_singlecol = 1; 19790150Smarkm f_longform = 0; 19896892Stjr f_stream = 0; 1991556Srgrimes break; 2001556Srgrimes case 'C': 20196892Stjr f_sortacross = f_longform = f_singlecol = 0; 2021556Srgrimes break; 2031556Srgrimes case 'l': 2041556Srgrimes f_longform = 1; 20590150Smarkm f_singlecol = 0; 20696892Stjr f_stream = 0; 2071556Srgrimes break; 20896892Stjr case 'x': 20996892Stjr f_sortacross = 1; 21096892Stjr f_longform = 0; 21196892Stjr f_singlecol = 0; 21296892Stjr break; 213157098Sjhb /* The -c, -u, and -U options override each other. */ 2141556Srgrimes case 'c': 2151556Srgrimes f_statustime = 1; 2161556Srgrimes f_accesstime = 0; 217157098Sjhb f_birthtime = 0; 2181556Srgrimes break; 2191556Srgrimes case 'u': 2201556Srgrimes f_accesstime = 1; 2211556Srgrimes f_statustime = 0; 222157098Sjhb f_birthtime = 0; 2231556Srgrimes break; 224157098Sjhb case 'U': 225157098Sjhb f_birthtime = 1; 226157098Sjhb f_accesstime = 0; 227157098Sjhb f_statustime = 0; 228157098Sjhb break; 229242722Sgrog case 'a': 230242722Sgrog fts_options |= FTS_SEEDOT; 231242722Sgrog /* FALLTHROUGH */ 232242722Sgrog case 'A': 233242722Sgrog f_listdot = 1; 234242722Sgrog break; 235242722Sgrog /* The -t and -S options override each other. */ 236242722Sgrog case 'S': 237242722Sgrog f_sizesort = 1; 238242722Sgrog f_timesort = 0; 239242722Sgrog break; 240242722Sgrog case 't': 241242722Sgrog f_timesort = 1; 242242722Sgrog f_sizesort = 0; 243242722Sgrog break; 244242840Speter /* Other flags. Please keep alphabetic. */ 245242725Sgrog case ',': 246242725Sgrog f_thousands = 1; 247242725Sgrog break; 248242722Sgrog case 'B': 249242722Sgrog f_nonprint = 0; 250242722Sgrog f_octal = 1; 251242722Sgrog f_octal_escape = 0; 252242722Sgrog break; 253242807Sgrog case 'D': 254242807Sgrog f_timeformat = optarg; 255242807Sgrog break; 256242807Sgrog case 'F': 2571556Srgrimes f_type = 1; 25896892Stjr f_slash = 0; 2591556Srgrimes break; 260242722Sgrog case 'G': 261242722Sgrog setenv("CLICOLOR", "", 1); 262242722Sgrog break; 26335426Sdes case 'H': 26488602Sjoe fts_options |= FTS_COMFOLLOW; 265203665Sjh f_nofollow = 0; 26635426Sdes break; 267242722Sgrog case 'I': 268242722Sgrog f_noautodot = 1; 26961178Sjoe break; 2701556Srgrimes case 'L': 2711556Srgrimes fts_options &= ~FTS_PHYSICAL; 2721556Srgrimes fts_options |= FTS_LOGICAL; 273203665Sjh f_nofollow = 0; 2741556Srgrimes break; 27535426Sdes case 'P': 27688602Sjoe fts_options &= ~FTS_COMFOLLOW; 27735426Sdes fts_options &= ~FTS_LOGICAL; 27835426Sdes fts_options |= FTS_PHYSICAL; 279203665Sjh f_nofollow = 1; 28035426Sdes break; 2811556Srgrimes case 'R': 2821556Srgrimes f_recursive = 1; 2831556Srgrimes break; 284242722Sgrog case 'T': 285242722Sgrog f_sectime = 1; 2861556Srgrimes break; 287242722Sgrog case 'W': 288242722Sgrog f_whiteout = 1; 289152256Smux break; 290242722Sgrog case 'Z': 291242722Sgrog f_label = 1; 292242722Sgrog break; 293242722Sgrog case 'b': 294242722Sgrog f_nonprint = 0; 295242722Sgrog f_octal = 0; 296242722Sgrog f_octal_escape = 1; 297242722Sgrog break; 2981556Srgrimes /* The -d option turns off the -R option. */ 2991556Srgrimes case 'd': 3001556Srgrimes f_listdir = 1; 3011556Srgrimes f_recursive = 0; 3021556Srgrimes break; 3031556Srgrimes case 'f': 3041556Srgrimes f_nosort = 1; 3051556Srgrimes break; 30688602Sjoe case 'g': /* Compatibility with 4.3BSD. */ 3071556Srgrimes break; 30888591Sjoe case 'h': 30988591Sjoe f_humanval = 1; 31088591Sjoe break; 3111556Srgrimes case 'i': 3121556Srgrimes f_inode = 1; 3131556Srgrimes break; 3142889Spst case 'k': 315123089Sobrien f_humanval = 0; 3162889Spst f_kblocks = 1; 3172889Spst break; 31896892Stjr case 'm': 31996892Stjr f_stream = 1; 32096892Stjr f_singlecol = 0; 32196892Stjr f_longform = 0; 32296892Stjr break; 32349373Ssheldonh case 'n': 32449373Ssheldonh f_numericonly = 1; 32549373Ssheldonh break; 3261556Srgrimes case 'o': 3271556Srgrimes f_flags = 1; 3281556Srgrimes break; 32996892Stjr case 'p': 33096892Stjr f_slash = 1; 33196892Stjr f_type = 1; 33296892Stjr break; 3331556Srgrimes case 'q': 3341556Srgrimes f_nonprint = 1; 33535373Sdes f_octal = 0; 33688602Sjoe f_octal_escape = 0; 3371556Srgrimes break; 3381556Srgrimes case 'r': 3391556Srgrimes f_reversesort = 1; 3401556Srgrimes break; 3411556Srgrimes case 's': 3421556Srgrimes f_size = 1; 3431556Srgrimes break; 34488603Sjoe case 'w': 34588603Sjoe f_nonprint = 0; 34688603Sjoe f_octal = 0; 34788603Sjoe f_octal_escape = 0; 34888603Sjoe break; 349242725Sgrog case 'y': 350242725Sgrog f_samesort = 1; 351242725Sgrog break; 3521556Srgrimes default: 3531556Srgrimes case '?': 3541556Srgrimes usage(); 3551556Srgrimes } 3561556Srgrimes } 3571556Srgrimes argc -= optind; 3581556Srgrimes argv += optind; 3591556Srgrimes 360152469Sru /* Root is -A automatically unless -I. */ 361152469Sru if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot) 362152256Smux f_listdot = 1; 363152256Smux 36464568Sjoe /* Enabling of colours is conditional on the environment. */ 36564568Sjoe if (getenv("CLICOLOR") && 36664568Sjoe (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 36764604Sjoe#ifdef COLORLS 36864568Sjoe if (tgetent(termcapbuf, getenv("TERM")) == 1) { 36964568Sjoe ansi_fgcol = tgetstr("AF", &bp); 37064568Sjoe ansi_bgcol = tgetstr("AB", &bp); 37188583Sjoe attrs_off = tgetstr("me", &bp); 37288583Sjoe enter_bold = tgetstr("md", &bp); 37364568Sjoe 37464568Sjoe /* To switch colours off use 'op' if 37564568Sjoe * available, otherwise use 'oc', or 37664568Sjoe * don't do colours at all. */ 37764568Sjoe ansi_coloff = tgetstr("op", &bp); 37864568Sjoe if (!ansi_coloff) 37964568Sjoe ansi_coloff = tgetstr("oc", &bp); 38064568Sjoe if (ansi_fgcol && ansi_bgcol && ansi_coloff) 38164568Sjoe f_color = 1; 38264568Sjoe } 38364604Sjoe#else 384106479Stjr warnx("color support not compiled in"); 38564604Sjoe#endif /*COLORLS*/ 38664568Sjoe 38764604Sjoe#ifdef COLORLS 38861289Sache if (f_color) { 38961337Sache /* 39061337Sache * We can't put tabs and color sequences together: 39161337Sache * column number will be incremented incorrectly 39261337Sache * for "stty oxtabs" mode. 39361337Sache */ 39461337Sache f_notabs = 1; 39588602Sjoe (void)signal(SIGINT, colorquit); 39688602Sjoe (void)signal(SIGQUIT, colorquit); 39761178Sjoe parsecolors(getenv("LSCOLORS")); 39861289Sache } 39961268Sjoe#endif 40061178Sjoe 4011556Srgrimes /* 402146924Sdd * If not -F, -i, -l, -s, -S or -t options, don't require stat 40361178Sjoe * information, unless in color mode in which case we do 40461178Sjoe * need this to determine which colors to display. 4051556Srgrimes */ 406146924Sdd if (!f_inode && !f_longform && !f_size && !f_timesort && 407146924Sdd !f_sizesort && !f_type 40861268Sjoe#ifdef COLORLS 40961268Sjoe && !f_color 41061268Sjoe#endif 41188602Sjoe ) 4121556Srgrimes fts_options |= FTS_NOSTAT; 4131556Srgrimes 4141556Srgrimes /* 415203665Sjh * If not -F, -P, -d or -l options, follow any symbolic links listed on 4161556Srgrimes * the command line. 4171556Srgrimes */ 418203665Sjh if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash)) 4191556Srgrimes fts_options |= FTS_COMFOLLOW; 4201556Srgrimes 42120417Ssteve /* 42220417Ssteve * If -W, show whiteout entries 42320417Ssteve */ 42420417Ssteve#ifdef FTS_WHITEOUT 42520417Ssteve if (f_whiteout) 42620417Ssteve fts_options |= FTS_WHITEOUT; 42720417Ssteve#endif 42820417Ssteve 429226546Sdes /* If -i, -l or -s, figure out block size. */ 430226546Sdes if (f_inode || f_longform || f_size) { 4312889Spst if (f_kblocks) 4327282Sphk blocksize = 2; 4337282Sphk else { 4347282Sphk (void)getbsize(¬used, &blocksize); 4357282Sphk blocksize /= 512; 4367282Sphk } 4371556Srgrimes } 4381556Srgrimes /* Select a sort function. */ 4391556Srgrimes if (f_reversesort) { 440146924Sdd if (!f_timesort && !f_sizesort) 4411556Srgrimes sortfcn = revnamecmp; 442157100Sjhb else if (f_sizesort) 443157100Sjhb sortfcn = revsizecmp; 4441556Srgrimes else if (f_accesstime) 4451556Srgrimes sortfcn = revacccmp; 446157098Sjhb else if (f_birthtime) 447157098Sjhb sortfcn = revbirthcmp; 4481556Srgrimes else if (f_statustime) 4491556Srgrimes sortfcn = revstatcmp; 45088602Sjoe else /* Use modification time. */ 4511556Srgrimes sortfcn = revmodcmp; 4521556Srgrimes } else { 453146924Sdd if (!f_timesort && !f_sizesort) 4541556Srgrimes sortfcn = namecmp; 455157100Sjhb else if (f_sizesort) 456157100Sjhb sortfcn = sizecmp; 4571556Srgrimes else if (f_accesstime) 4581556Srgrimes sortfcn = acccmp; 459157098Sjhb else if (f_birthtime) 460157098Sjhb sortfcn = birthcmp; 4611556Srgrimes else if (f_statustime) 4621556Srgrimes sortfcn = statcmp; 46388602Sjoe else /* Use modification time. */ 4641556Srgrimes sortfcn = modcmp; 4651556Srgrimes } 4661556Srgrimes 4671556Srgrimes /* Select a print function. */ 4681556Srgrimes if (f_singlecol) 4691556Srgrimes printfcn = printscol; 4701556Srgrimes else if (f_longform) 4711556Srgrimes printfcn = printlong; 47296892Stjr else if (f_stream) 47396892Stjr printfcn = printstream; 4741556Srgrimes else 4751556Srgrimes printfcn = printcol; 4761556Srgrimes 4771556Srgrimes if (argc) 4781556Srgrimes traverse(argc, argv, fts_options); 4791556Srgrimes else 4801556Srgrimes traverse(1, dotav, fts_options); 48117852Sadam exit(rval); 4821556Srgrimes} 4831556Srgrimes 48488602Sjoestatic int output; /* If anything output. */ 4851556Srgrimes 4861556Srgrimes/* 4871556Srgrimes * Traverse() walks the logical directory structure specified by the argv list 4881556Srgrimes * in the order specified by the mastercmp() comparison function. During the 4891556Srgrimes * traversal it passes linked lists of structures to display() which represent 4901556Srgrimes * a superset (may be exact set) of the files to be displayed. 4911556Srgrimes */ 4921556Srgrimesstatic void 49390110Simptraverse(int argc, char *argv[], int options) 4941556Srgrimes{ 4951556Srgrimes FTS *ftsp; 4961556Srgrimes FTSENT *p, *chp; 4971556Srgrimes int ch_options; 4981556Srgrimes 4991556Srgrimes if ((ftsp = 5001556Srgrimes fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 50199744Sdillon err(1, "fts_open"); 5021556Srgrimes 503130237Sdas /* 504130237Sdas * We ignore errors from fts_children here since they will be 505130237Sdas * replicated and signalled on the next call to fts_read() below. 506130237Sdas */ 507130237Sdas chp = fts_children(ftsp, 0); 508130237Sdas if (chp != NULL) 509130237Sdas display(NULL, chp, options); 5101556Srgrimes if (f_listdir) 5111556Srgrimes return; 5121556Srgrimes 5131556Srgrimes /* 5141556Srgrimes * If not recursing down this tree and don't need stat info, just get 5151556Srgrimes * the names. 5161556Srgrimes */ 517108057Srwatson ch_options = !f_recursive && !f_label && 518108057Srwatson options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 5191556Srgrimes 5201556Srgrimes while ((p = fts_read(ftsp)) != NULL) 5211556Srgrimes switch (p->fts_info) { 5221556Srgrimes case FTS_DC: 5231556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 5241556Srgrimes break; 5251556Srgrimes case FTS_DNR: 5261556Srgrimes case FTS_ERR: 527202944Sjh warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 52817852Sadam rval = 1; 5291556Srgrimes break; 5301556Srgrimes case FTS_D: 5311556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 532152469Sru p->fts_name[0] == '.' && !f_listdot) 5331556Srgrimes break; 5341556Srgrimes 5351556Srgrimes /* 5361556Srgrimes * If already output something, put out a newline as 537242840Speter * a separator. If multiple arguments, precede each 5381556Srgrimes * directory with its name. 5391556Srgrimes */ 540105390Stjr if (output) { 541105390Stjr putchar('\n'); 542114583Smarkm (void)printname(p->fts_path); 543105390Stjr puts(":"); 544105390Stjr } else if (argc > 1) { 545114583Smarkm (void)printname(p->fts_path); 546105390Stjr puts(":"); 5471556Srgrimes output = 1; 5481556Srgrimes } 5491556Srgrimes chp = fts_children(ftsp, ch_options); 550105832Srwatson display(p, chp, options); 5511556Srgrimes 5521556Srgrimes if (!f_recursive && chp != NULL) 5531556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 5541556Srgrimes break; 55596681Sbillf default: 55696681Sbillf break; 5571556Srgrimes } 5581556Srgrimes if (errno) 5591556Srgrimes err(1, "fts_read"); 5601556Srgrimes} 5611556Srgrimes 5621556Srgrimes/* 5631556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 5641556Srgrimes * along with any other necessary information to the print function. P 5651556Srgrimes * points to the parent directory of the display list. 5661556Srgrimes */ 5671556Srgrimesstatic void 568114583Smarkmdisplay(const FTSENT *p, FTSENT *list, int options) 5691556Srgrimes{ 5701556Srgrimes struct stat *sp; 5711556Srgrimes DISPLAY d; 5721556Srgrimes FTSENT *cur; 5731556Srgrimes NAMES *np; 57490150Smarkm off_t maxsize; 575114583Smarkm long maxblock; 576241014Smdf uintmax_t maxinode; 577241014Smdf u_long btotal, labelstrlen, maxlen, maxnlink; 578105832Srwatson u_long maxlabelstr; 579225847Sed u_int sizelen; 580202945Sjh int maxflags; 58190150Smarkm gid_t maxgroup; 58290150Smarkm uid_t maxuser; 58390150Smarkm size_t flen, ulen, glen; 58437932Shoek char *initmax; 5851556Srgrimes int entries, needstats; 58696649Sjmallett const char *user, *group; 587105832Srwatson char *flags, *labelstr = NULL; 58850050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 58950050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5901556Srgrimes 5911556Srgrimes needstats = f_inode || f_longform || f_size; 5921556Srgrimes flen = 0; 59337932Shoek btotal = 0; 59437932Shoek initmax = getenv("LS_COLWIDTHS"); 59537932Shoek /* Fields match -lios order. New ones should be added at the end. */ 596241014Smdf maxlabelstr = maxblock = maxlen = maxnlink = 0; 597241014Smdf maxuser = maxgroup = maxflags = maxsize = 0; 598241014Smdf maxinode = 0; 59937932Shoek if (initmax != NULL && *initmax != '\0') { 60037932Shoek char *initmax2, *jinitmax; 60137932Shoek int ninitmax; 60237932Shoek 60337932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 604114583Smarkm jinitmax = malloc(strlen(initmax) * 2 + 2); 60537932Shoek if (jinitmax == NULL) 60699744Sdillon err(1, "malloc"); 607114583Smarkm initmax2 = jinitmax; 60837932Shoek if (*initmax == ':') 60937932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 61037932Shoek else 61137932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 61237932Shoek for (initmax++; *initmax != '\0'; initmax++) { 61337932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 61437932Shoek *initmax2++ = '0'; 61537932Shoek *initmax2++ = initmax[0]; 61637932Shoek initmax2[1] = '\0'; 61737932Shoek } else { 61837932Shoek *initmax2++ = initmax[0]; 61937932Shoek initmax2[1] = '\0'; 62037932Shoek } 62137932Shoek } 62288602Sjoe if (initmax2[-1] == ':') 62388602Sjoe strcpy(initmax2, "0"); 62437932Shoek 62537932Shoek ninitmax = sscanf(jinitmax, 626241014Smdf " %ju : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", 62737932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 628105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 62937932Shoek f_notabs = 1; 63037932Shoek switch (ninitmax) { 63188602Sjoe case 0: 63288602Sjoe maxinode = 0; 633102410Scharnier /* FALLTHROUGH */ 63488602Sjoe case 1: 63588602Sjoe maxblock = 0; 636102410Scharnier /* FALLTHROUGH */ 63788602Sjoe case 2: 63888602Sjoe maxnlink = 0; 639102410Scharnier /* FALLTHROUGH */ 64088602Sjoe case 3: 64188602Sjoe maxuser = 0; 642102410Scharnier /* FALLTHROUGH */ 64388602Sjoe case 4: 64488602Sjoe maxgroup = 0; 645102410Scharnier /* FALLTHROUGH */ 64688602Sjoe case 5: 64788602Sjoe maxflags = 0; 648102410Scharnier /* FALLTHROUGH */ 64988602Sjoe case 6: 65088602Sjoe maxsize = 0; 651102410Scharnier /* FALLTHROUGH */ 65288602Sjoe case 7: 65388602Sjoe maxlen = 0; 654102410Scharnier /* FALLTHROUGH */ 65588602Sjoe case 8: 656105832Srwatson maxlabelstr = 0; 657102410Scharnier /* FALLTHROUGH */ 65861338Sache#ifdef COLORLS 65988602Sjoe if (!f_color) 66061337Sache#endif 66188602Sjoe f_notabs = 0; 662102410Scharnier /* FALLTHROUGH */ 66390150Smarkm default: 66496681Sbillf break; 66537932Shoek } 666114583Smarkm MAKENINES(maxinode); 667114583Smarkm MAKENINES(maxblock); 668114583Smarkm MAKENINES(maxnlink); 669114583Smarkm MAKENINES(maxsize); 670130029Sle free(jinitmax); 67190150Smarkm } 672225847Sed d.s_size = 0; 673225847Sed sizelen = 0; 6747165Sjoerg flags = NULL; 6751556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6761556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6771556Srgrimes warnx("%s: %s", 6781556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6791556Srgrimes cur->fts_number = NO_PRINT; 68017852Sadam rval = 1; 6811556Srgrimes continue; 6821556Srgrimes } 6831556Srgrimes /* 6841556Srgrimes * P is NULL if list is the argv list, to which different rules 6851556Srgrimes * apply. 6861556Srgrimes */ 6871556Srgrimes if (p == NULL) { 6881556Srgrimes /* Directories will be displayed later. */ 6891556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6901556Srgrimes cur->fts_number = NO_PRINT; 6911556Srgrimes continue; 6921556Srgrimes } 6931556Srgrimes } else { 6941556Srgrimes /* Only display dot file if -a/-A set. */ 695152469Sru if (cur->fts_name[0] == '.' && !f_listdot) { 6961556Srgrimes cur->fts_number = NO_PRINT; 6971556Srgrimes continue; 6981556Srgrimes } 6991556Srgrimes } 7001556Srgrimes if (cur->fts_namelen > maxlen) 7011556Srgrimes maxlen = cur->fts_namelen; 70235417Sdes if (f_octal || f_octal_escape) { 70388602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 70488602Sjoe 70588602Sjoe if (t > maxlen) 70688602Sjoe maxlen = t; 70737932Shoek } 7081556Srgrimes if (needstats) { 7091556Srgrimes sp = cur->fts_statp; 7101556Srgrimes if (sp->st_blocks > maxblock) 7111556Srgrimes maxblock = sp->st_blocks; 7121556Srgrimes if (sp->st_ino > maxinode) 7131556Srgrimes maxinode = sp->st_ino; 7141556Srgrimes if (sp->st_nlink > maxnlink) 7151556Srgrimes maxnlink = sp->st_nlink; 7161556Srgrimes if (sp->st_size > maxsize) 7171556Srgrimes maxsize = sp->st_size; 7181556Srgrimes 7191556Srgrimes btotal += sp->st_blocks; 7201556Srgrimes if (f_longform) { 72149373Ssheldonh if (f_numericonly) { 72249373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 72349373Ssheldonh "%u", sp->st_uid); 72449373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 72549373Ssheldonh "%u", sp->st_gid); 72649373Ssheldonh user = nuser; 72749373Ssheldonh group = ngroup; 72849373Ssheldonh } else { 72949373Ssheldonh user = user_from_uid(sp->st_uid, 0); 73049373Ssheldonh group = group_from_gid(sp->st_gid, 0); 73149373Ssheldonh } 7321556Srgrimes if ((ulen = strlen(user)) > maxuser) 7331556Srgrimes maxuser = ulen; 7341556Srgrimes if ((glen = strlen(group)) > maxgroup) 7351556Srgrimes maxgroup = glen; 7361556Srgrimes if (f_flags) { 73761749Sjoe flags = fflagstostr(sp->st_flags); 73861749Sjoe if (flags != NULL && *flags == '\0') { 73961749Sjoe free(flags); 74061749Sjoe flags = strdup("-"); 74161749Sjoe } 74261749Sjoe if (flags == NULL) 74399744Sdillon err(1, "fflagstostr"); 74490150Smarkm flen = strlen(flags); 74590150Smarkm if (flen > (size_t)maxflags) 7461556Srgrimes maxflags = flen; 7471556Srgrimes } else 7481556Srgrimes flen = 0; 749105832Srwatson labelstr = NULL; 750105832Srwatson if (f_label) { 751105836Srwatson char name[PATH_MAX + 1]; 752105832Srwatson mac_t label; 753105832Srwatson int error; 754105832Srwatson 755105832Srwatson error = mac_prepare_file_label(&label); 756105832Srwatson if (error == -1) { 757108057Srwatson warn("MAC label for %s/%s", 758108057Srwatson cur->fts_parent->fts_path, 759108057Srwatson cur->fts_name); 760105832Srwatson goto label_out; 761105832Srwatson } 762105832Srwatson 763105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 764105836Srwatson snprintf(name, sizeof(name), 765105836Srwatson "%s", cur->fts_name); 766105836Srwatson else 767105836Srwatson snprintf(name, sizeof(name), 768108057Srwatson "%s/%s", cur->fts_parent-> 769108057Srwatson fts_accpath, cur->fts_name); 770105836Srwatson 771105832Srwatson if (options & FTS_LOGICAL) 772105836Srwatson error = mac_get_file(name, 773105836Srwatson label); 774105832Srwatson else 775105836Srwatson error = mac_get_link(name, 776105836Srwatson label); 777105832Srwatson if (error == -1) { 778108057Srwatson warn("MAC label for %s/%s", 779108057Srwatson cur->fts_parent->fts_path, 780108057Srwatson cur->fts_name); 781105832Srwatson mac_free(label); 782105832Srwatson goto label_out; 783105832Srwatson } 784105832Srwatson 785105832Srwatson error = mac_to_text(label, 786105832Srwatson &labelstr); 787105832Srwatson if (error == -1) { 788108057Srwatson warn("MAC label for %s/%s", 789108057Srwatson cur->fts_parent->fts_path, 790108057Srwatson cur->fts_name); 791105832Srwatson mac_free(label); 792105832Srwatson goto label_out; 793105832Srwatson } 794105832Srwatson mac_free(label); 795105832Srwatsonlabel_out: 796105832Srwatson if (labelstr == NULL) 797114047Srwatson labelstr = strdup("-"); 798105832Srwatson labelstrlen = strlen(labelstr); 799105832Srwatson if (labelstrlen > maxlabelstr) 800105832Srwatson maxlabelstr = labelstrlen; 80186922Sgreen } else 802105832Srwatson labelstrlen = 0; 8031556Srgrimes 804105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 80586922Sgreen ulen + glen + flen + 4)) == NULL) 80699744Sdillon err(1, "malloc"); 8071556Srgrimes 8081556Srgrimes np->user = &np->data[0]; 8091556Srgrimes (void)strcpy(np->user, user); 8101556Srgrimes np->group = &np->data[ulen + 1]; 8111556Srgrimes (void)strcpy(np->group, group); 8121556Srgrimes 813225847Sed if (S_ISCHR(sp->st_mode) || 814225847Sed S_ISBLK(sp->st_mode)) { 815225847Sed sizelen = snprintf(NULL, 0, 816225847Sed "%#jx", (uintmax_t)sp->st_rdev); 817225847Sed if (d.s_size < sizelen) 818225847Sed d.s_size = sizelen; 819202945Sjh } 8201556Srgrimes 8211556Srgrimes if (f_flags) { 8221556Srgrimes np->flags = &np->data[ulen + glen + 2]; 82388602Sjoe (void)strcpy(np->flags, flags); 82461749Sjoe free(flags); 8251556Srgrimes } 826105832Srwatson if (f_label) { 827105832Srwatson np->label = &np->data[ulen + glen + 2 82886922Sgreen + (f_flags ? flen + 1 : 0)]; 829105832Srwatson (void)strcpy(np->label, labelstr); 830105832Srwatson free(labelstr); 83186922Sgreen } 8321556Srgrimes cur->fts_pointer = np; 8331556Srgrimes } 8341556Srgrimes } 8351556Srgrimes ++entries; 8361556Srgrimes } 8371556Srgrimes 838130237Sdas /* 839130237Sdas * If there are no entries to display, we normally stop right 840130237Sdas * here. However, we must continue if we have to display the 841130237Sdas * total block count. In this case, we display the total only 842130237Sdas * on the second (p != NULL) pass. 843130237Sdas */ 844130237Sdas if (!entries && (!(f_longform || f_size) || p == NULL)) 8451556Srgrimes return; 8461556Srgrimes 8471556Srgrimes d.list = list; 8481556Srgrimes d.entries = entries; 8491556Srgrimes d.maxlen = maxlen; 8501556Srgrimes if (needstats) { 8511556Srgrimes d.btotal = btotal; 852226509Sdes d.s_block = snprintf(NULL, 0, "%lu", howmany(maxblock, blocksize)); 8531556Srgrimes d.s_flags = maxflags; 854105832Srwatson d.s_label = maxlabelstr; 8551556Srgrimes d.s_group = maxgroup; 856241014Smdf d.s_inode = snprintf(NULL, 0, "%ju", maxinode); 857225847Sed d.s_nlink = snprintf(NULL, 0, "%lu", maxnlink); 858225847Sed sizelen = f_humanval ? HUMANVALSTR_LEN : 859225847Sed snprintf(NULL, 0, "%ju", maxsize); 860225847Sed if (d.s_size < sizelen) 861225847Sed d.s_size = sizelen; 8621556Srgrimes d.s_user = maxuser; 8631556Srgrimes } 864242807Sgrog if (f_thousands) /* make space for commas */ 865242725Sgrog d.s_size += (d.s_size - 1) / 3; 8661556Srgrimes printfcn(&d); 8671556Srgrimes output = 1; 8681556Srgrimes 8691556Srgrimes if (f_longform) 8701556Srgrimes for (cur = list; cur; cur = cur->fts_link) 8711556Srgrimes free(cur->fts_pointer); 8721556Srgrimes} 8731556Srgrimes 8741556Srgrimes/* 8751556Srgrimes * Ordering for mastercmp: 8761556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 877242840Speter * as larger than directories. Within either group, use the sort function. 8781556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8791556Srgrimes */ 8801556Srgrimesstatic int 881103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8821556Srgrimes{ 8831556Srgrimes int a_info, b_info; 8841556Srgrimes 8851556Srgrimes a_info = (*a)->fts_info; 8861556Srgrimes if (a_info == FTS_ERR) 8871556Srgrimes return (0); 8881556Srgrimes b_info = (*b)->fts_info; 8891556Srgrimes if (b_info == FTS_ERR) 8901556Srgrimes return (0); 8911556Srgrimes 8921556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8931556Srgrimes return (namecmp(*a, *b)); 8941556Srgrimes 89529560Ssef if (a_info != b_info && 89629560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8971556Srgrimes if (a_info == FTS_D) 8981556Srgrimes return (1); 89929560Ssef if (b_info == FTS_D) 9001556Srgrimes return (-1); 90129560Ssef } 90229560Ssef return (sortfcn(*a, *b)); 9031556Srgrimes} 904