ls.c revision 123089
11556Srgrimes/* 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 * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3827958Sstevestatic const char copyright[] = 391556Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 401556Srgrimes The Regents of the University of California. All rights reserved.\n"; 4127967Ssteve#endif /* not lint */ 4227967Ssteve 4390153Smarkm#if 0 4427967Ssteve#ifndef lint 4527967Sstevestatic char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; 4690153Smarkm#endif /* not lint */ 4727967Ssteve#endif 4899109Sobrien#include <sys/cdefs.h> 4999109Sobrien__FBSDID("$FreeBSD: head/bin/ls/ls.c 123089 2003-12-01 19:10:29Z obrien $"); 501556Srgrimes 511556Srgrimes#include <sys/types.h> 521556Srgrimes#include <sys/stat.h> 531556Srgrimes#include <sys/ioctl.h> 54105832Srwatson#include <sys/mac.h> 551556Srgrimes 561556Srgrimes#include <dirent.h> 571556Srgrimes#include <err.h> 581556Srgrimes#include <errno.h> 591556Srgrimes#include <fts.h> 6090878Simp#include <grp.h> 61114583Smarkm#include <inttypes.h> 6250050Ssheldonh#include <limits.h> 6350050Ssheldonh#include <locale.h> 6490878Simp#include <pwd.h> 651556Srgrimes#include <stdio.h> 661556Srgrimes#include <stdlib.h> 671556Srgrimes#include <string.h> 6861289Sache#include <unistd.h> 6961268Sjoe#ifdef COLORLS 7061289Sache#include <termcap.h> 7161289Sache#include <signal.h> 7261268Sjoe#endif 731556Srgrimes 741556Srgrimes#include "ls.h" 751556Srgrimes#include "extern.h" 761556Srgrimes 7750050Ssheldonh/* 7850050Ssheldonh * Upward approximation of the maximum number of characters needed to 7950050Ssheldonh * represent a value of integral type t as a string, excluding the 8050050Ssheldonh * NUL terminator, with provision for a sign. 8150050Ssheldonh */ 8250051Ssheldonh#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) 8350050Ssheldonh 84114583Smarkm/* 85114583Smarkm * MAKENINES(n) turns n into (10**n)-1. This is useful for converting a width 86114583Smarkm * into a number that wide in decimal. 87114583Smarkm * XXX: Overflows are not considered. 88114583Smarkm */ 89114583Smarkm#define MAKENINES(n) \ 90114583Smarkm do { \ 91114583Smarkm intmax_t i; \ 92114583Smarkm \ 93114583Smarkm /* Use a loop as all values of n are small. */ \ 94114583Smarkm for (i = 1; n > 0; i *= 10) \ 95114583Smarkm n--; \ 96114583Smarkm n = i - 1; \ 97114583Smarkm } while(0) 98114583Smarkm 99114583Smarkmstatic void display(const FTSENT *, FTSENT *, int); 100103726Swollmanstatic int mastercmp(const FTSENT * const *, const FTSENT * const *); 10190110Simpstatic void traverse(int, char **, int); 1021556Srgrimes 103114583Smarkmstatic void (*printfcn)(const DISPLAY *); 10490110Simpstatic int (*sortfcn)(const FTSENT *, const FTSENT *); 1051556Srgrimes 1061556Srgrimeslong blocksize; /* block size units */ 1071556Srgrimesint termwidth = 80; /* default terminal width */ 1081556Srgrimes 1091556Srgrimes/* flags */ 11090150Smarkm int f_accesstime; /* use time of last access */ 11190150Smarkm int f_flags; /* show flags associated with a file */ 11290150Smarkm int f_humanval; /* show human-readable file sizes */ 11390150Smarkm int f_inode; /* print inode */ 11490150Smarkmstatic int f_kblocks; /* print size in kilobytes */ 11590150Smarkmstatic int f_listdir; /* list actual directory, not contents */ 11690150Smarkmstatic int f_listdot; /* list files beginning with . */ 11790150Smarkm int f_longform; /* long listing format */ 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 */ 12690150Smarkm int f_sectime; /* print the real time for all files */ 12790150Smarkmstatic int f_singlecol; /* use single column output */ 12890150Smarkm int f_size; /* list size in short listing */ 12996892Stjr int f_slash; /* similar to f_type, but only for dirs */ 13096892Stjr int f_sortacross; /* sort across rows, not down columns */ 13190150Smarkm int f_statustime; /* use time of last mode change */ 132114583Smarkmstatic int f_stream; /* stream the output, separate with commas */ 13390150Smarkmstatic int f_timesort; /* sort by time vice name */ 13490150Smarkm int f_type; /* add type character for non-regular files */ 13590150Smarkmstatic int f_whiteout; /* show whiteout entries */ 136105832Srwatson int f_label; /* show MAC label */ 13761268Sjoe#ifdef COLORLS 13890150Smarkm int f_color; /* add type in color for non-regular files */ 13961271Sjoe 14061271Sjoechar *ansi_bgcol; /* ANSI sequence to set background colour */ 14161271Sjoechar *ansi_fgcol; /* ANSI sequence to set foreground colour */ 14261271Sjoechar *ansi_coloff; /* ANSI sequence to reset colours */ 14388583Sjoechar *attrs_off; /* ANSI sequence to turn off attributes */ 14488583Sjoechar *enter_bold; /* ANSI sequence to set color to bold mode */ 14561268Sjoe#endif 1461556Srgrimes 14790150Smarkmstatic int rval; 14817852Sadam 1491556Srgrimesint 15090110Simpmain(int argc, char *argv[]) 1511556Srgrimes{ 15288602Sjoe static char dot[] = ".", *dotav[] = {dot, NULL}; 1531556Srgrimes struct winsize win; 1541556Srgrimes int ch, fts_options, notused; 1551556Srgrimes char *p; 15661271Sjoe#ifdef COLORLS 15788602Sjoe char termcapbuf[1024]; /* termcap definition buffer */ 15888602Sjoe char tcapbuf[512]; /* capability buffer */ 15961271Sjoe char *bp = tcapbuf; 16061271Sjoe#endif 16161271Sjoe 16288602Sjoe (void)setlocale(LC_ALL, ""); 16311808Sache 1641556Srgrimes /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 1651556Srgrimes if (isatty(STDOUT_FILENO)) { 16697803Stjr termwidth = 80; 16797803Stjr if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 16897803Stjr termwidth = atoi(p); 16997803Stjr else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 17097803Stjr win.ws_col > 0) 1711556Srgrimes termwidth = win.ws_col; 17290150Smarkm f_nonprint = 1; 1735158Sjoerg } else { 1741556Srgrimes f_singlecol = 1; 1755158Sjoerg /* retrieve environment variable, in case of explicit -C */ 17690150Smarkm p = getenv("COLUMNS"); 17790150Smarkm if (p) 1785158Sjoerg termwidth = atoi(p); 1795158Sjoerg } 1801556Srgrimes 1811556Srgrimes /* Root is -A automatically. */ 1821556Srgrimes if (!getuid()) 1831556Srgrimes f_listdot = 1; 1841556Srgrimes 1851556Srgrimes fts_options = FTS_PHYSICAL; 18696892Stjr while ((ch = getopt(argc, argv, "1ABCFGHLPRTWZabcdfghiklmnopqrstuwx")) 18796892Stjr != -1) { 1881556Srgrimes switch (ch) { 1891556Srgrimes /* 19096892Stjr * The -1, -C, -x and -l options all override each other so 19196892Stjr * shell aliasing works right. 1921556Srgrimes */ 1931556Srgrimes case '1': 1941556Srgrimes f_singlecol = 1; 19590150Smarkm f_longform = 0; 19696892Stjr f_stream = 0; 1971556Srgrimes break; 19835417Sdes case 'B': 19935417Sdes f_nonprint = 0; 20035417Sdes f_octal = 1; 20188602Sjoe f_octal_escape = 0; 20235417Sdes break; 2031556Srgrimes case 'C': 20496892Stjr f_sortacross = f_longform = f_singlecol = 0; 2051556Srgrimes break; 2061556Srgrimes case 'l': 2071556Srgrimes f_longform = 1; 20890150Smarkm f_singlecol = 0; 20996892Stjr f_stream = 0; 2101556Srgrimes break; 21196892Stjr case 'x': 21296892Stjr f_sortacross = 1; 21396892Stjr f_longform = 0; 21496892Stjr f_singlecol = 0; 21596892Stjr break; 2161556Srgrimes /* The -c and -u options override each other. */ 2171556Srgrimes case 'c': 2181556Srgrimes f_statustime = 1; 2191556Srgrimes f_accesstime = 0; 2201556Srgrimes break; 2211556Srgrimes case 'u': 2221556Srgrimes f_accesstime = 1; 2231556Srgrimes f_statustime = 0; 2241556Srgrimes break; 2251556Srgrimes case 'F': 2261556Srgrimes f_type = 1; 22796892Stjr f_slash = 0; 2281556Srgrimes break; 22935426Sdes case 'H': 23088602Sjoe fts_options |= FTS_COMFOLLOW; 23135426Sdes break; 23261178Sjoe case 'G': 23364568Sjoe setenv("CLICOLOR", "", 1); 23461178Sjoe break; 2351556Srgrimes case 'L': 2361556Srgrimes fts_options &= ~FTS_PHYSICAL; 2371556Srgrimes fts_options |= FTS_LOGICAL; 2381556Srgrimes break; 23935426Sdes case 'P': 24088602Sjoe fts_options &= ~FTS_COMFOLLOW; 24135426Sdes fts_options &= ~FTS_LOGICAL; 24235426Sdes fts_options |= FTS_PHYSICAL; 24335426Sdes break; 2441556Srgrimes case 'R': 2451556Srgrimes f_recursive = 1; 2461556Srgrimes break; 2471556Srgrimes case 'a': 2481556Srgrimes fts_options |= FTS_SEEDOT; 2491556Srgrimes /* FALLTHROUGH */ 2501556Srgrimes case 'A': 2511556Srgrimes f_listdot = 1; 2521556Srgrimes break; 2531556Srgrimes /* The -d option turns off the -R option. */ 2541556Srgrimes case 'd': 2551556Srgrimes f_listdir = 1; 2561556Srgrimes f_recursive = 0; 2571556Srgrimes break; 2581556Srgrimes case 'f': 2591556Srgrimes f_nosort = 1; 2601556Srgrimes break; 26188602Sjoe case 'g': /* Compatibility with 4.3BSD. */ 2621556Srgrimes break; 26388591Sjoe case 'h': 26488591Sjoe f_humanval = 1; 26588591Sjoe break; 2661556Srgrimes case 'i': 2671556Srgrimes f_inode = 1; 2681556Srgrimes break; 2692889Spst case 'k': 270123089Sobrien f_humanval = 0; 2712889Spst f_kblocks = 1; 2722889Spst break; 27396892Stjr case 'm': 27496892Stjr f_stream = 1; 27596892Stjr f_singlecol = 0; 27696892Stjr f_longform = 0; 27796892Stjr break; 27849373Ssheldonh case 'n': 27949373Ssheldonh f_numericonly = 1; 28049373Ssheldonh break; 2811556Srgrimes case 'o': 2821556Srgrimes f_flags = 1; 2831556Srgrimes break; 28496892Stjr case 'p': 28596892Stjr f_slash = 1; 28696892Stjr f_type = 1; 28796892Stjr break; 2881556Srgrimes case 'q': 2891556Srgrimes f_nonprint = 1; 29035373Sdes f_octal = 0; 29188602Sjoe f_octal_escape = 0; 2921556Srgrimes break; 2931556Srgrimes case 'r': 2941556Srgrimes f_reversesort = 1; 2951556Srgrimes break; 2961556Srgrimes case 's': 2971556Srgrimes f_size = 1; 2981556Srgrimes break; 2991556Srgrimes case 'T': 3001556Srgrimes f_sectime = 1; 3011556Srgrimes break; 3021556Srgrimes case 't': 3031556Srgrimes f_timesort = 1; 3041556Srgrimes break; 30520417Ssteve case 'W': 30620417Ssteve f_whiteout = 1; 30720417Ssteve break; 30835373Sdes case 'b': 30935373Sdes f_nonprint = 0; 31088602Sjoe f_octal = 0; 31135417Sdes f_octal_escape = 1; 31235373Sdes break; 31388603Sjoe case 'w': 31488603Sjoe f_nonprint = 0; 31588603Sjoe f_octal = 0; 31688603Sjoe f_octal_escape = 0; 31788603Sjoe break; 31886922Sgreen case 'Z': 319105832Srwatson f_label = 1; 32086922Sgreen break; 3211556Srgrimes default: 3221556Srgrimes case '?': 3231556Srgrimes usage(); 3241556Srgrimes } 3251556Srgrimes } 3261556Srgrimes argc -= optind; 3271556Srgrimes argv += optind; 3281556Srgrimes 32964568Sjoe /* Enabling of colours is conditional on the environment. */ 33064568Sjoe if (getenv("CLICOLOR") && 33164568Sjoe (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 33264604Sjoe#ifdef COLORLS 33364568Sjoe if (tgetent(termcapbuf, getenv("TERM")) == 1) { 33464568Sjoe ansi_fgcol = tgetstr("AF", &bp); 33564568Sjoe ansi_bgcol = tgetstr("AB", &bp); 33688583Sjoe attrs_off = tgetstr("me", &bp); 33788583Sjoe enter_bold = tgetstr("md", &bp); 33864568Sjoe 33964568Sjoe /* To switch colours off use 'op' if 34064568Sjoe * available, otherwise use 'oc', or 34164568Sjoe * don't do colours at all. */ 34264568Sjoe ansi_coloff = tgetstr("op", &bp); 34364568Sjoe if (!ansi_coloff) 34464568Sjoe ansi_coloff = tgetstr("oc", &bp); 34564568Sjoe if (ansi_fgcol && ansi_bgcol && ansi_coloff) 34664568Sjoe f_color = 1; 34764568Sjoe } 34864604Sjoe#else 349106479Stjr warnx("color support not compiled in"); 35064604Sjoe#endif /*COLORLS*/ 35164568Sjoe 35264604Sjoe#ifdef COLORLS 35361289Sache if (f_color) { 35461337Sache /* 35561337Sache * We can't put tabs and color sequences together: 35661337Sache * column number will be incremented incorrectly 35761337Sache * for "stty oxtabs" mode. 35861337Sache */ 35961337Sache f_notabs = 1; 36088602Sjoe (void)signal(SIGINT, colorquit); 36188602Sjoe (void)signal(SIGQUIT, colorquit); 36261178Sjoe parsecolors(getenv("LSCOLORS")); 36361289Sache } 36461268Sjoe#endif 36561178Sjoe 3661556Srgrimes /* 3671556Srgrimes * If not -F, -i, -l, -s or -t options, don't require stat 36861178Sjoe * information, unless in color mode in which case we do 36961178Sjoe * need this to determine which colors to display. 3701556Srgrimes */ 37161178Sjoe if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type 37261268Sjoe#ifdef COLORLS 37361268Sjoe && !f_color 37461268Sjoe#endif 37588602Sjoe ) 3761556Srgrimes fts_options |= FTS_NOSTAT; 3771556Srgrimes 3781556Srgrimes /* 3791556Srgrimes * If not -F, -d or -l options, follow any symbolic links listed on 3801556Srgrimes * the command line. 3811556Srgrimes */ 3821556Srgrimes if (!f_longform && !f_listdir && !f_type) 3831556Srgrimes fts_options |= FTS_COMFOLLOW; 3841556Srgrimes 38520417Ssteve /* 38620417Ssteve * If -W, show whiteout entries 38720417Ssteve */ 38820417Ssteve#ifdef FTS_WHITEOUT 38920417Ssteve if (f_whiteout) 39020417Ssteve fts_options |= FTS_WHITEOUT; 39120417Ssteve#endif 39220417Ssteve 3931556Srgrimes /* If -l or -s, figure out block size. */ 3941556Srgrimes if (f_longform || f_size) { 3952889Spst if (f_kblocks) 3967282Sphk blocksize = 2; 3977282Sphk else { 3987282Sphk (void)getbsize(¬used, &blocksize); 3997282Sphk blocksize /= 512; 4007282Sphk } 4011556Srgrimes } 4021556Srgrimes /* Select a sort function. */ 4031556Srgrimes if (f_reversesort) { 4041556Srgrimes if (!f_timesort) 4051556Srgrimes sortfcn = revnamecmp; 4061556Srgrimes else if (f_accesstime) 4071556Srgrimes sortfcn = revacccmp; 4081556Srgrimes else if (f_statustime) 4091556Srgrimes sortfcn = revstatcmp; 41088602Sjoe else /* Use modification time. */ 4111556Srgrimes sortfcn = revmodcmp; 4121556Srgrimes } else { 4131556Srgrimes if (!f_timesort) 4141556Srgrimes sortfcn = namecmp; 4151556Srgrimes else if (f_accesstime) 4161556Srgrimes sortfcn = acccmp; 4171556Srgrimes else if (f_statustime) 4181556Srgrimes sortfcn = statcmp; 41988602Sjoe else /* Use modification time. */ 4201556Srgrimes sortfcn = modcmp; 4211556Srgrimes } 4221556Srgrimes 4231556Srgrimes /* Select a print function. */ 4241556Srgrimes if (f_singlecol) 4251556Srgrimes printfcn = printscol; 4261556Srgrimes else if (f_longform) 4271556Srgrimes printfcn = printlong; 42896892Stjr else if (f_stream) 42996892Stjr printfcn = printstream; 4301556Srgrimes else 4311556Srgrimes printfcn = printcol; 4321556Srgrimes 4331556Srgrimes if (argc) 4341556Srgrimes traverse(argc, argv, fts_options); 4351556Srgrimes else 4361556Srgrimes traverse(1, dotav, fts_options); 43717852Sadam exit(rval); 4381556Srgrimes} 4391556Srgrimes 44088602Sjoestatic int output; /* If anything output. */ 4411556Srgrimes 4421556Srgrimes/* 4431556Srgrimes * Traverse() walks the logical directory structure specified by the argv list 4441556Srgrimes * in the order specified by the mastercmp() comparison function. During the 4451556Srgrimes * traversal it passes linked lists of structures to display() which represent 4461556Srgrimes * a superset (may be exact set) of the files to be displayed. 4471556Srgrimes */ 4481556Srgrimesstatic void 44990110Simptraverse(int argc, char *argv[], int options) 4501556Srgrimes{ 4511556Srgrimes FTS *ftsp; 4521556Srgrimes FTSENT *p, *chp; 4531556Srgrimes int ch_options; 4541556Srgrimes 4551556Srgrimes if ((ftsp = 4561556Srgrimes fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 45799744Sdillon err(1, "fts_open"); 4581556Srgrimes 459105832Srwatson display(NULL, fts_children(ftsp, 0), options); 4601556Srgrimes if (f_listdir) 4611556Srgrimes return; 4621556Srgrimes 4631556Srgrimes /* 4641556Srgrimes * If not recursing down this tree and don't need stat info, just get 4651556Srgrimes * the names. 4661556Srgrimes */ 467108057Srwatson ch_options = !f_recursive && !f_label && 468108057Srwatson options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 4691556Srgrimes 4701556Srgrimes while ((p = fts_read(ftsp)) != NULL) 4711556Srgrimes switch (p->fts_info) { 4721556Srgrimes case FTS_DC: 4731556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 4741556Srgrimes break; 4751556Srgrimes case FTS_DNR: 4761556Srgrimes case FTS_ERR: 4771556Srgrimes warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 47817852Sadam rval = 1; 4791556Srgrimes break; 4801556Srgrimes case FTS_D: 4811556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 4821556Srgrimes p->fts_name[0] == '.' && !f_listdot) 4831556Srgrimes break; 4841556Srgrimes 4851556Srgrimes /* 4861556Srgrimes * If already output something, put out a newline as 4871556Srgrimes * a separator. If multiple arguments, precede each 4881556Srgrimes * directory with its name. 4891556Srgrimes */ 490105390Stjr if (output) { 491105390Stjr putchar('\n'); 492114583Smarkm (void)printname(p->fts_path); 493105390Stjr puts(":"); 494105390Stjr } else if (argc > 1) { 495114583Smarkm (void)printname(p->fts_path); 496105390Stjr puts(":"); 4971556Srgrimes output = 1; 4981556Srgrimes } 4991556Srgrimes chp = fts_children(ftsp, ch_options); 500105832Srwatson display(p, chp, options); 5011556Srgrimes 5021556Srgrimes if (!f_recursive && chp != NULL) 5031556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 5041556Srgrimes break; 50596681Sbillf default: 50696681Sbillf break; 5071556Srgrimes } 5081556Srgrimes if (errno) 5091556Srgrimes err(1, "fts_read"); 5101556Srgrimes} 5111556Srgrimes 5121556Srgrimes/* 5131556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 5141556Srgrimes * along with any other necessary information to the print function. P 5151556Srgrimes * points to the parent directory of the display list. 5161556Srgrimes */ 5171556Srgrimesstatic void 518114583Smarkmdisplay(const FTSENT *p, FTSENT *list, int options) 5191556Srgrimes{ 5201556Srgrimes struct stat *sp; 5211556Srgrimes DISPLAY d; 5221556Srgrimes FTSENT *cur; 5231556Srgrimes NAMES *np; 52490150Smarkm off_t maxsize; 525114583Smarkm long maxblock; 526114583Smarkm u_long btotal, labelstrlen, maxinode, maxlen, maxnlink; 527105832Srwatson u_long maxlabelstr; 52890150Smarkm int bcfile, maxflags; 52990150Smarkm gid_t maxgroup; 53090150Smarkm uid_t maxuser; 53190150Smarkm size_t flen, ulen, glen; 53237932Shoek char *initmax; 5331556Srgrimes int entries, needstats; 53496649Sjmallett const char *user, *group; 535105832Srwatson char *flags, *labelstr = NULL; 53650050Ssheldonh char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 53750050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 53850050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5391556Srgrimes 5401556Srgrimes /* 5411556Srgrimes * If list is NULL there are two possibilities: that the parent 5421556Srgrimes * directory p has no children, or that fts_children() returned an 5431556Srgrimes * error. We ignore the error case since it will be replicated 5441556Srgrimes * on the next call to fts_read() on the post-order visit to the 54546684Skris * directory p, and will be signaled in traverse(). 5461556Srgrimes */ 5471556Srgrimes if (list == NULL) 5481556Srgrimes return; 5491556Srgrimes 5501556Srgrimes needstats = f_inode || f_longform || f_size; 5511556Srgrimes flen = 0; 55237932Shoek btotal = 0; 55337932Shoek initmax = getenv("LS_COLWIDTHS"); 55437932Shoek /* Fields match -lios order. New ones should be added at the end. */ 555105832Srwatson maxlabelstr = maxblock = maxinode = maxlen = maxnlink = 55690150Smarkm maxuser = maxgroup = maxflags = maxsize = 0; 55737932Shoek if (initmax != NULL && *initmax != '\0') { 55837932Shoek char *initmax2, *jinitmax; 55937932Shoek int ninitmax; 56037932Shoek 56137932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 562114583Smarkm jinitmax = malloc(strlen(initmax) * 2 + 2); 56337932Shoek if (jinitmax == NULL) 56499744Sdillon err(1, "malloc"); 565114583Smarkm initmax2 = jinitmax; 56637932Shoek if (*initmax == ':') 56737932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 56837932Shoek else 56937932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 57037932Shoek for (initmax++; *initmax != '\0'; initmax++) { 57137932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 57237932Shoek *initmax2++ = '0'; 57337932Shoek *initmax2++ = initmax[0]; 57437932Shoek initmax2[1] = '\0'; 57537932Shoek } else { 57637932Shoek *initmax2++ = initmax[0]; 57737932Shoek initmax2[1] = '\0'; 57837932Shoek } 57937932Shoek } 58088602Sjoe if (initmax2[-1] == ':') 58188602Sjoe strcpy(initmax2, "0"); 58237932Shoek 58337932Shoek ninitmax = sscanf(jinitmax, 584114583Smarkm " %lu : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", 58537932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 586105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 58737932Shoek f_notabs = 1; 58837932Shoek switch (ninitmax) { 58988602Sjoe case 0: 59088602Sjoe maxinode = 0; 591102410Scharnier /* FALLTHROUGH */ 59288602Sjoe case 1: 59388602Sjoe maxblock = 0; 594102410Scharnier /* FALLTHROUGH */ 59588602Sjoe case 2: 59688602Sjoe maxnlink = 0; 597102410Scharnier /* FALLTHROUGH */ 59888602Sjoe case 3: 59988602Sjoe maxuser = 0; 600102410Scharnier /* FALLTHROUGH */ 60188602Sjoe case 4: 60288602Sjoe maxgroup = 0; 603102410Scharnier /* FALLTHROUGH */ 60488602Sjoe case 5: 60588602Sjoe maxflags = 0; 606102410Scharnier /* FALLTHROUGH */ 60788602Sjoe case 6: 60888602Sjoe maxsize = 0; 609102410Scharnier /* FALLTHROUGH */ 61088602Sjoe case 7: 61188602Sjoe maxlen = 0; 612102410Scharnier /* FALLTHROUGH */ 61388602Sjoe case 8: 614105832Srwatson maxlabelstr = 0; 615102410Scharnier /* FALLTHROUGH */ 61661338Sache#ifdef COLORLS 61788602Sjoe if (!f_color) 61861337Sache#endif 61988602Sjoe f_notabs = 0; 620102410Scharnier /* FALLTHROUGH */ 62190150Smarkm default: 62296681Sbillf break; 62337932Shoek } 624114583Smarkm MAKENINES(maxinode); 625114583Smarkm MAKENINES(maxblock); 626114583Smarkm MAKENINES(maxnlink); 627114583Smarkm MAKENINES(maxsize); 62890150Smarkm } 6291556Srgrimes bcfile = 0; 6307165Sjoerg flags = NULL; 6311556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6321556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6331556Srgrimes warnx("%s: %s", 6341556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6351556Srgrimes cur->fts_number = NO_PRINT; 63617852Sadam rval = 1; 6371556Srgrimes continue; 6381556Srgrimes } 6391556Srgrimes /* 6401556Srgrimes * P is NULL if list is the argv list, to which different rules 6411556Srgrimes * apply. 6421556Srgrimes */ 6431556Srgrimes if (p == NULL) { 6441556Srgrimes /* Directories will be displayed later. */ 6451556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6461556Srgrimes cur->fts_number = NO_PRINT; 6471556Srgrimes continue; 6481556Srgrimes } 6491556Srgrimes } else { 6501556Srgrimes /* Only display dot file if -a/-A set. */ 6511556Srgrimes if (cur->fts_name[0] == '.' && !f_listdot) { 6521556Srgrimes cur->fts_number = NO_PRINT; 6531556Srgrimes continue; 6541556Srgrimes } 6551556Srgrimes } 6561556Srgrimes if (cur->fts_namelen > maxlen) 6571556Srgrimes maxlen = cur->fts_namelen; 65835417Sdes if (f_octal || f_octal_escape) { 65988602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 66088602Sjoe 66188602Sjoe if (t > maxlen) 66288602Sjoe maxlen = t; 66337932Shoek } 6641556Srgrimes if (needstats) { 6651556Srgrimes sp = cur->fts_statp; 6661556Srgrimes if (sp->st_blocks > maxblock) 6671556Srgrimes maxblock = sp->st_blocks; 6681556Srgrimes if (sp->st_ino > maxinode) 6691556Srgrimes maxinode = sp->st_ino; 6701556Srgrimes if (sp->st_nlink > maxnlink) 6711556Srgrimes maxnlink = sp->st_nlink; 6721556Srgrimes if (sp->st_size > maxsize) 6731556Srgrimes maxsize = sp->st_size; 6741556Srgrimes 6751556Srgrimes btotal += sp->st_blocks; 6761556Srgrimes if (f_longform) { 67749373Ssheldonh if (f_numericonly) { 67849373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 67949373Ssheldonh "%u", sp->st_uid); 68049373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 68149373Ssheldonh "%u", sp->st_gid); 68249373Ssheldonh user = nuser; 68349373Ssheldonh group = ngroup; 68449373Ssheldonh } else { 68549373Ssheldonh user = user_from_uid(sp->st_uid, 0); 68649373Ssheldonh group = group_from_gid(sp->st_gid, 0); 68749373Ssheldonh } 6881556Srgrimes if ((ulen = strlen(user)) > maxuser) 6891556Srgrimes maxuser = ulen; 6901556Srgrimes if ((glen = strlen(group)) > maxgroup) 6911556Srgrimes maxgroup = glen; 6921556Srgrimes if (f_flags) { 69361749Sjoe flags = fflagstostr(sp->st_flags); 69461749Sjoe if (flags != NULL && *flags == '\0') { 69561749Sjoe free(flags); 69661749Sjoe flags = strdup("-"); 69761749Sjoe } 69861749Sjoe if (flags == NULL) 69999744Sdillon err(1, "fflagstostr"); 70090150Smarkm flen = strlen(flags); 70190150Smarkm if (flen > (size_t)maxflags) 7021556Srgrimes maxflags = flen; 7031556Srgrimes } else 7041556Srgrimes flen = 0; 705105832Srwatson labelstr = NULL; 706105832Srwatson if (f_label) { 707105836Srwatson char name[PATH_MAX + 1]; 708105832Srwatson mac_t label; 709105832Srwatson int error; 710105832Srwatson 711105832Srwatson error = mac_prepare_file_label(&label); 712105832Srwatson if (error == -1) { 713108057Srwatson warn("MAC label for %s/%s", 714108057Srwatson cur->fts_parent->fts_path, 715108057Srwatson cur->fts_name); 716105832Srwatson goto label_out; 717105832Srwatson } 718105832Srwatson 719105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 720105836Srwatson snprintf(name, sizeof(name), 721105836Srwatson "%s", cur->fts_name); 722105836Srwatson else 723105836Srwatson snprintf(name, sizeof(name), 724108057Srwatson "%s/%s", cur->fts_parent-> 725108057Srwatson fts_accpath, cur->fts_name); 726105836Srwatson 727105832Srwatson if (options & FTS_LOGICAL) 728105836Srwatson error = mac_get_file(name, 729105836Srwatson label); 730105832Srwatson else 731105836Srwatson error = mac_get_link(name, 732105836Srwatson label); 733105832Srwatson if (error == -1) { 734108057Srwatson warn("MAC label for %s/%s", 735108057Srwatson cur->fts_parent->fts_path, 736108057Srwatson cur->fts_name); 737105832Srwatson mac_free(label); 738105832Srwatson goto label_out; 739105832Srwatson } 740105832Srwatson 741105832Srwatson error = mac_to_text(label, 742105832Srwatson &labelstr); 743105832Srwatson if (error == -1) { 744108057Srwatson warn("MAC label for %s/%s", 745108057Srwatson cur->fts_parent->fts_path, 746108057Srwatson cur->fts_name); 747105832Srwatson mac_free(label); 748105832Srwatson goto label_out; 749105832Srwatson } 750105832Srwatson mac_free(label); 751105832Srwatsonlabel_out: 752105832Srwatson if (labelstr == NULL) 753114047Srwatson labelstr = strdup("-"); 754105832Srwatson labelstrlen = strlen(labelstr); 755105832Srwatson if (labelstrlen > maxlabelstr) 756105832Srwatson maxlabelstr = labelstrlen; 75786922Sgreen } else 758105832Srwatson labelstrlen = 0; 7591556Srgrimes 760105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 76186922Sgreen ulen + glen + flen + 4)) == NULL) 76299744Sdillon err(1, "malloc"); 7631556Srgrimes 7641556Srgrimes np->user = &np->data[0]; 7651556Srgrimes (void)strcpy(np->user, user); 7661556Srgrimes np->group = &np->data[ulen + 1]; 7671556Srgrimes (void)strcpy(np->group, group); 7681556Srgrimes 7691556Srgrimes if (S_ISCHR(sp->st_mode) || 7701556Srgrimes S_ISBLK(sp->st_mode)) 7711556Srgrimes bcfile = 1; 7721556Srgrimes 7731556Srgrimes if (f_flags) { 7741556Srgrimes np->flags = &np->data[ulen + glen + 2]; 77588602Sjoe (void)strcpy(np->flags, flags); 77661749Sjoe free(flags); 7771556Srgrimes } 778105832Srwatson if (f_label) { 779105832Srwatson np->label = &np->data[ulen + glen + 2 78086922Sgreen + (f_flags ? flen + 1 : 0)]; 781105832Srwatson (void)strcpy(np->label, labelstr); 782105832Srwatson free(labelstr); 78386922Sgreen } 7841556Srgrimes cur->fts_pointer = np; 7851556Srgrimes } 7861556Srgrimes } 7871556Srgrimes ++entries; 7881556Srgrimes } 7891556Srgrimes 7901556Srgrimes if (!entries) 7911556Srgrimes return; 7921556Srgrimes 7931556Srgrimes d.list = list; 7941556Srgrimes d.entries = entries; 7951556Srgrimes d.maxlen = maxlen; 7961556Srgrimes if (needstats) { 7971556Srgrimes d.bcfile = bcfile; 7981556Srgrimes d.btotal = btotal; 7991556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 8001556Srgrimes d.s_block = strlen(buf); 8011556Srgrimes d.s_flags = maxflags; 802105832Srwatson d.s_label = maxlabelstr; 8031556Srgrimes d.s_group = maxgroup; 8041556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 8051556Srgrimes d.s_inode = strlen(buf); 8061556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 8071556Srgrimes d.s_nlink = strlen(buf); 808114583Smarkm (void)snprintf(buf, sizeof(buf), "%ju", maxsize); 8091556Srgrimes d.s_size = strlen(buf); 8101556Srgrimes d.s_user = maxuser; 8111556Srgrimes } 8121556Srgrimes printfcn(&d); 8131556Srgrimes output = 1; 8141556Srgrimes 8151556Srgrimes if (f_longform) 8161556Srgrimes for (cur = list; cur; cur = cur->fts_link) 8171556Srgrimes free(cur->fts_pointer); 8181556Srgrimes} 8191556Srgrimes 8201556Srgrimes/* 8211556Srgrimes * Ordering for mastercmp: 8221556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 8231556Srgrimes * as larger than directories. Within either group, use the sort function. 8241556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8251556Srgrimes */ 8261556Srgrimesstatic int 827103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8281556Srgrimes{ 8291556Srgrimes int a_info, b_info; 8301556Srgrimes 8311556Srgrimes a_info = (*a)->fts_info; 8321556Srgrimes if (a_info == FTS_ERR) 8331556Srgrimes return (0); 8341556Srgrimes b_info = (*b)->fts_info; 8351556Srgrimes if (b_info == FTS_ERR) 8361556Srgrimes return (0); 8371556Srgrimes 8381556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8391556Srgrimes return (namecmp(*a, *b)); 8401556Srgrimes 84129560Ssef if (a_info != b_info && 84229560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8431556Srgrimes if (a_info == FTS_D) 8441556Srgrimes return (1); 84529560Ssef if (b_info == FTS_D) 8461556Srgrimes return (-1); 84729560Ssef } 84829560Ssef return (sortfcn(*a, *b)); 8491556Srgrimes} 850