ls.c revision 157098
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: head/bin/ls/ls.c 157098 2006-03-24 16:38:02Z jhb $"); 461556Srgrimes 471556Srgrimes#include <sys/types.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/* 81114583Smarkm * 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 */ 11290150Smarkmstatic int f_listdir; /* list actual directory, not contents */ 11390150Smarkmstatic int f_listdot; /* list files beginning with . */ 114152469Srustatic int f_noautodot; /* do not automatically enable -A for root */ 11590150Smarkm int f_longform; /* long listing format */ 11690150Smarkm int f_nonprint; /* show unprintables as ? */ 11790150Smarkmstatic int f_nosort; /* don't sort output */ 11890150Smarkm int f_notabs; /* don't use tab-separated multi-col output */ 11990150Smarkmstatic int f_numericonly; /* don't convert uid/gid to name */ 12090150Smarkm int f_octal; /* show unprintables as \xxx */ 12190150Smarkm int f_octal_escape; /* like f_octal but use C escapes if possible */ 12290150Smarkmstatic int f_recursive; /* ls subdirectories also */ 12390150Smarkmstatic int f_reversesort; /* reverse whatever sort is used */ 12490150Smarkm int f_sectime; /* print the real time for all files */ 12590150Smarkmstatic int f_singlecol; /* use single column output */ 12690150Smarkm int f_size; /* list size in short listing */ 12796892Stjr int f_slash; /* similar to f_type, but only for dirs */ 12896892Stjr int f_sortacross; /* sort across rows, not down columns */ 12990150Smarkm int f_statustime; /* use time of last mode change */ 130114583Smarkmstatic int f_stream; /* stream the output, separate with commas */ 13190150Smarkmstatic int f_timesort; /* sort by time vice name */ 132146924Sddstatic int f_sizesort; 13390150Smarkm int f_type; /* add type character for non-regular files */ 13490150Smarkmstatic int f_whiteout; /* show whiteout entries */ 135105832Srwatson int f_label; /* show MAC label */ 13661268Sjoe#ifdef COLORLS 13790150Smarkm int f_color; /* add type in color for non-regular files */ 13861271Sjoe 13961271Sjoechar *ansi_bgcol; /* ANSI sequence to set background colour */ 14061271Sjoechar *ansi_fgcol; /* ANSI sequence to set foreground colour */ 14161271Sjoechar *ansi_coloff; /* ANSI sequence to reset colours */ 14288583Sjoechar *attrs_off; /* ANSI sequence to turn off attributes */ 14388583Sjoechar *enter_bold; /* ANSI sequence to set color to bold mode */ 14461268Sjoe#endif 1451556Srgrimes 14690150Smarkmstatic int rval; 14717852Sadam 1481556Srgrimesint 14990110Simpmain(int argc, char *argv[]) 1501556Srgrimes{ 15188602Sjoe static char dot[] = ".", *dotav[] = {dot, NULL}; 1521556Srgrimes struct winsize win; 1531556Srgrimes int ch, fts_options, notused; 1541556Srgrimes char *p; 15561271Sjoe#ifdef COLORLS 15688602Sjoe char termcapbuf[1024]; /* termcap definition buffer */ 15788602Sjoe char tcapbuf[512]; /* capability buffer */ 15861271Sjoe char *bp = tcapbuf; 15961271Sjoe#endif 16061271Sjoe 16188602Sjoe (void)setlocale(LC_ALL, ""); 16211808Sache 1631556Srgrimes /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 1641556Srgrimes if (isatty(STDOUT_FILENO)) { 16597803Stjr termwidth = 80; 16697803Stjr if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 16797803Stjr termwidth = atoi(p); 16897803Stjr else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 16997803Stjr win.ws_col > 0) 1701556Srgrimes termwidth = win.ws_col; 17190150Smarkm f_nonprint = 1; 1725158Sjoerg } else { 1731556Srgrimes f_singlecol = 1; 1745158Sjoerg /* retrieve environment variable, in case of explicit -C */ 17590150Smarkm p = getenv("COLUMNS"); 17690150Smarkm if (p) 1775158Sjoerg termwidth = atoi(p); 1785158Sjoerg } 1791556Srgrimes 1801556Srgrimes fts_options = FTS_PHYSICAL; 181146924Sdd while ((ch = getopt(argc, argv, 182157098Sjhb "1ABCFGHILPRSTUWZabcdfghiklmnopqrstuwx")) != -1) { 1831556Srgrimes switch (ch) { 1841556Srgrimes /* 18596892Stjr * The -1, -C, -x and -l options all override each other so 18696892Stjr * shell aliasing works right. 1871556Srgrimes */ 1881556Srgrimes case '1': 1891556Srgrimes f_singlecol = 1; 19090150Smarkm f_longform = 0; 19196892Stjr f_stream = 0; 1921556Srgrimes break; 19335417Sdes case 'B': 19435417Sdes f_nonprint = 0; 19535417Sdes f_octal = 1; 19688602Sjoe f_octal_escape = 0; 19735417Sdes break; 1981556Srgrimes case 'C': 19996892Stjr f_sortacross = f_longform = f_singlecol = 0; 2001556Srgrimes break; 2011556Srgrimes case 'l': 2021556Srgrimes f_longform = 1; 20390150Smarkm f_singlecol = 0; 20496892Stjr f_stream = 0; 2051556Srgrimes break; 20696892Stjr case 'x': 20796892Stjr f_sortacross = 1; 20896892Stjr f_longform = 0; 20996892Stjr f_singlecol = 0; 21096892Stjr break; 211157098Sjhb /* The -c, -u, and -U options override each other. */ 2121556Srgrimes case 'c': 2131556Srgrimes f_statustime = 1; 2141556Srgrimes f_accesstime = 0; 215157098Sjhb f_birthtime = 0; 2161556Srgrimes break; 2171556Srgrimes case 'u': 2181556Srgrimes f_accesstime = 1; 2191556Srgrimes f_statustime = 0; 220157098Sjhb f_birthtime = 0; 2211556Srgrimes break; 222157098Sjhb case 'U': 223157098Sjhb f_birthtime = 1; 224157098Sjhb f_accesstime = 0; 225157098Sjhb f_statustime = 0; 226157098Sjhb break; 2271556Srgrimes case 'F': 2281556Srgrimes f_type = 1; 22996892Stjr f_slash = 0; 2301556Srgrimes break; 23135426Sdes case 'H': 23288602Sjoe fts_options |= FTS_COMFOLLOW; 23335426Sdes break; 23461178Sjoe case 'G': 23564568Sjoe setenv("CLICOLOR", "", 1); 23661178Sjoe break; 2371556Srgrimes case 'L': 2381556Srgrimes fts_options &= ~FTS_PHYSICAL; 2391556Srgrimes fts_options |= FTS_LOGICAL; 2401556Srgrimes break; 24135426Sdes case 'P': 24288602Sjoe fts_options &= ~FTS_COMFOLLOW; 24335426Sdes fts_options &= ~FTS_LOGICAL; 24435426Sdes fts_options |= FTS_PHYSICAL; 24535426Sdes break; 2461556Srgrimes case 'R': 2471556Srgrimes f_recursive = 1; 2481556Srgrimes break; 2491556Srgrimes case 'a': 2501556Srgrimes fts_options |= FTS_SEEDOT; 251152469Sru /* FALLTHROUGH */ 2521556Srgrimes case 'A': 2531556Srgrimes f_listdot = 1; 2541556Srgrimes break; 255152256Smux case 'I': 256152469Sru f_noautodot = 1; 257152256Smux break; 2581556Srgrimes /* The -d option turns off the -R option. */ 2591556Srgrimes case 'd': 2601556Srgrimes f_listdir = 1; 2611556Srgrimes f_recursive = 0; 2621556Srgrimes break; 2631556Srgrimes case 'f': 2641556Srgrimes f_nosort = 1; 2651556Srgrimes break; 26688602Sjoe case 'g': /* Compatibility with 4.3BSD. */ 2671556Srgrimes break; 26888591Sjoe case 'h': 26988591Sjoe f_humanval = 1; 27088591Sjoe break; 2711556Srgrimes case 'i': 2721556Srgrimes f_inode = 1; 2731556Srgrimes break; 2742889Spst case 'k': 275123089Sobrien f_humanval = 0; 2762889Spst f_kblocks = 1; 2772889Spst break; 27896892Stjr case 'm': 27996892Stjr f_stream = 1; 28096892Stjr f_singlecol = 0; 28196892Stjr f_longform = 0; 28296892Stjr break; 28349373Ssheldonh case 'n': 28449373Ssheldonh f_numericonly = 1; 28549373Ssheldonh break; 2861556Srgrimes case 'o': 2871556Srgrimes f_flags = 1; 2881556Srgrimes break; 28996892Stjr case 'p': 29096892Stjr f_slash = 1; 29196892Stjr f_type = 1; 29296892Stjr break; 2931556Srgrimes case 'q': 2941556Srgrimes f_nonprint = 1; 29535373Sdes f_octal = 0; 29688602Sjoe f_octal_escape = 0; 2971556Srgrimes break; 2981556Srgrimes case 'r': 2991556Srgrimes f_reversesort = 1; 3001556Srgrimes break; 3011556Srgrimes case 's': 3021556Srgrimes f_size = 1; 3031556Srgrimes break; 3041556Srgrimes case 'T': 3051556Srgrimes f_sectime = 1; 3061556Srgrimes break; 3071556Srgrimes case 't': 3081556Srgrimes f_timesort = 1; 3091556Srgrimes break; 310146924Sdd case 'S': 311146924Sdd f_sizesort = 1; 312146924Sdd break; 31320417Ssteve case 'W': 31420417Ssteve f_whiteout = 1; 31520417Ssteve break; 31635373Sdes case 'b': 31735373Sdes f_nonprint = 0; 31888602Sjoe f_octal = 0; 31935417Sdes f_octal_escape = 1; 32035373Sdes break; 32188603Sjoe case 'w': 32288603Sjoe f_nonprint = 0; 32388603Sjoe f_octal = 0; 32488603Sjoe f_octal_escape = 0; 32588603Sjoe break; 32686922Sgreen case 'Z': 327105832Srwatson f_label = 1; 32886922Sgreen break; 3291556Srgrimes default: 3301556Srgrimes case '?': 3311556Srgrimes usage(); 3321556Srgrimes } 3331556Srgrimes } 3341556Srgrimes argc -= optind; 3351556Srgrimes argv += optind; 3361556Srgrimes 337152469Sru /* Root is -A automatically unless -I. */ 338152469Sru if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot) 339152256Smux f_listdot = 1; 340152256Smux 34164568Sjoe /* Enabling of colours is conditional on the environment. */ 34264568Sjoe if (getenv("CLICOLOR") && 34364568Sjoe (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 34464604Sjoe#ifdef COLORLS 34564568Sjoe if (tgetent(termcapbuf, getenv("TERM")) == 1) { 34664568Sjoe ansi_fgcol = tgetstr("AF", &bp); 34764568Sjoe ansi_bgcol = tgetstr("AB", &bp); 34888583Sjoe attrs_off = tgetstr("me", &bp); 34988583Sjoe enter_bold = tgetstr("md", &bp); 35064568Sjoe 35164568Sjoe /* To switch colours off use 'op' if 35264568Sjoe * available, otherwise use 'oc', or 35364568Sjoe * don't do colours at all. */ 35464568Sjoe ansi_coloff = tgetstr("op", &bp); 35564568Sjoe if (!ansi_coloff) 35664568Sjoe ansi_coloff = tgetstr("oc", &bp); 35764568Sjoe if (ansi_fgcol && ansi_bgcol && ansi_coloff) 35864568Sjoe f_color = 1; 35964568Sjoe } 36064604Sjoe#else 361106479Stjr warnx("color support not compiled in"); 36264604Sjoe#endif /*COLORLS*/ 36364568Sjoe 36464604Sjoe#ifdef COLORLS 36561289Sache if (f_color) { 36661337Sache /* 36761337Sache * We can't put tabs and color sequences together: 36861337Sache * column number will be incremented incorrectly 36961337Sache * for "stty oxtabs" mode. 37061337Sache */ 37161337Sache f_notabs = 1; 37288602Sjoe (void)signal(SIGINT, colorquit); 37388602Sjoe (void)signal(SIGQUIT, colorquit); 37461178Sjoe parsecolors(getenv("LSCOLORS")); 37561289Sache } 37661268Sjoe#endif 37761178Sjoe 3781556Srgrimes /* 379146924Sdd * If not -F, -i, -l, -s, -S or -t options, don't require stat 38061178Sjoe * information, unless in color mode in which case we do 38161178Sjoe * need this to determine which colors to display. 3821556Srgrimes */ 383146924Sdd if (!f_inode && !f_longform && !f_size && !f_timesort && 384146924Sdd !f_sizesort && !f_type 38561268Sjoe#ifdef COLORLS 38661268Sjoe && !f_color 38761268Sjoe#endif 38888602Sjoe ) 3891556Srgrimes fts_options |= FTS_NOSTAT; 3901556Srgrimes 3911556Srgrimes /* 3921556Srgrimes * If not -F, -d or -l options, follow any symbolic links listed on 3931556Srgrimes * the command line. 3941556Srgrimes */ 3951556Srgrimes if (!f_longform && !f_listdir && !f_type) 3961556Srgrimes fts_options |= FTS_COMFOLLOW; 3971556Srgrimes 39820417Ssteve /* 39920417Ssteve * If -W, show whiteout entries 40020417Ssteve */ 40120417Ssteve#ifdef FTS_WHITEOUT 40220417Ssteve if (f_whiteout) 40320417Ssteve fts_options |= FTS_WHITEOUT; 40420417Ssteve#endif 40520417Ssteve 4061556Srgrimes /* If -l or -s, figure out block size. */ 4071556Srgrimes if (f_longform || f_size) { 4082889Spst if (f_kblocks) 4097282Sphk blocksize = 2; 4107282Sphk else { 4117282Sphk (void)getbsize(¬used, &blocksize); 4127282Sphk blocksize /= 512; 4137282Sphk } 4141556Srgrimes } 4151556Srgrimes /* Select a sort function. */ 4161556Srgrimes if (f_reversesort) { 417146924Sdd if (!f_timesort && !f_sizesort) 4181556Srgrimes sortfcn = revnamecmp; 4191556Srgrimes else if (f_accesstime) 4201556Srgrimes sortfcn = revacccmp; 421157098Sjhb else if (f_birthtime) 422157098Sjhb sortfcn = revbirthcmp; 4231556Srgrimes else if (f_statustime) 4241556Srgrimes sortfcn = revstatcmp; 425146924Sdd else if (f_sizesort) 426146924Sdd sortfcn = revsizecmp; 42788602Sjoe else /* Use modification time. */ 4281556Srgrimes sortfcn = revmodcmp; 4291556Srgrimes } else { 430146924Sdd if (!f_timesort && !f_sizesort) 4311556Srgrimes sortfcn = namecmp; 4321556Srgrimes else if (f_accesstime) 4331556Srgrimes sortfcn = acccmp; 434157098Sjhb else if (f_birthtime) 435157098Sjhb sortfcn = birthcmp; 4361556Srgrimes else if (f_statustime) 4371556Srgrimes sortfcn = statcmp; 438146924Sdd else if (f_sizesort) 439146924Sdd sortfcn = sizecmp; 44088602Sjoe else /* Use modification time. */ 4411556Srgrimes sortfcn = modcmp; 4421556Srgrimes } 4431556Srgrimes 4441556Srgrimes /* Select a print function. */ 4451556Srgrimes if (f_singlecol) 4461556Srgrimes printfcn = printscol; 4471556Srgrimes else if (f_longform) 4481556Srgrimes printfcn = printlong; 44996892Stjr else if (f_stream) 45096892Stjr printfcn = printstream; 4511556Srgrimes else 4521556Srgrimes printfcn = printcol; 4531556Srgrimes 4541556Srgrimes if (argc) 4551556Srgrimes traverse(argc, argv, fts_options); 4561556Srgrimes else 4571556Srgrimes traverse(1, dotav, fts_options); 45817852Sadam exit(rval); 4591556Srgrimes} 4601556Srgrimes 46188602Sjoestatic int output; /* If anything output. */ 4621556Srgrimes 4631556Srgrimes/* 4641556Srgrimes * Traverse() walks the logical directory structure specified by the argv list 4651556Srgrimes * in the order specified by the mastercmp() comparison function. During the 4661556Srgrimes * traversal it passes linked lists of structures to display() which represent 4671556Srgrimes * a superset (may be exact set) of the files to be displayed. 4681556Srgrimes */ 4691556Srgrimesstatic void 47090110Simptraverse(int argc, char *argv[], int options) 4711556Srgrimes{ 4721556Srgrimes FTS *ftsp; 4731556Srgrimes FTSENT *p, *chp; 4741556Srgrimes int ch_options; 4751556Srgrimes 4761556Srgrimes if ((ftsp = 4771556Srgrimes fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 47899744Sdillon err(1, "fts_open"); 4791556Srgrimes 480130237Sdas /* 481130237Sdas * We ignore errors from fts_children here since they will be 482130237Sdas * replicated and signalled on the next call to fts_read() below. 483130237Sdas */ 484130237Sdas chp = fts_children(ftsp, 0); 485130237Sdas if (chp != NULL) 486130237Sdas display(NULL, chp, options); 4871556Srgrimes if (f_listdir) 4881556Srgrimes return; 4891556Srgrimes 4901556Srgrimes /* 4911556Srgrimes * If not recursing down this tree and don't need stat info, just get 4921556Srgrimes * the names. 4931556Srgrimes */ 494108057Srwatson ch_options = !f_recursive && !f_label && 495108057Srwatson options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 4961556Srgrimes 4971556Srgrimes while ((p = fts_read(ftsp)) != NULL) 4981556Srgrimes switch (p->fts_info) { 4991556Srgrimes case FTS_DC: 5001556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 5011556Srgrimes break; 5021556Srgrimes case FTS_DNR: 5031556Srgrimes case FTS_ERR: 5041556Srgrimes warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 50517852Sadam rval = 1; 5061556Srgrimes break; 5071556Srgrimes case FTS_D: 5081556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 509152469Sru p->fts_name[0] == '.' && !f_listdot) 5101556Srgrimes break; 5111556Srgrimes 5121556Srgrimes /* 5131556Srgrimes * If already output something, put out a newline as 5141556Srgrimes * a separator. If multiple arguments, precede each 5151556Srgrimes * directory with its name. 5161556Srgrimes */ 517105390Stjr if (output) { 518105390Stjr putchar('\n'); 519114583Smarkm (void)printname(p->fts_path); 520105390Stjr puts(":"); 521105390Stjr } else if (argc > 1) { 522114583Smarkm (void)printname(p->fts_path); 523105390Stjr puts(":"); 5241556Srgrimes output = 1; 5251556Srgrimes } 5261556Srgrimes chp = fts_children(ftsp, ch_options); 527105832Srwatson display(p, chp, options); 5281556Srgrimes 5291556Srgrimes if (!f_recursive && chp != NULL) 5301556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 5311556Srgrimes break; 53296681Sbillf default: 53396681Sbillf break; 5341556Srgrimes } 5351556Srgrimes if (errno) 5361556Srgrimes err(1, "fts_read"); 5371556Srgrimes} 5381556Srgrimes 5391556Srgrimes/* 5401556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 5411556Srgrimes * along with any other necessary information to the print function. P 5421556Srgrimes * points to the parent directory of the display list. 5431556Srgrimes */ 5441556Srgrimesstatic void 545114583Smarkmdisplay(const FTSENT *p, FTSENT *list, int options) 5461556Srgrimes{ 5471556Srgrimes struct stat *sp; 5481556Srgrimes DISPLAY d; 5491556Srgrimes FTSENT *cur; 5501556Srgrimes NAMES *np; 55190150Smarkm off_t maxsize; 552114583Smarkm long maxblock; 553114583Smarkm u_long btotal, labelstrlen, maxinode, maxlen, maxnlink; 554105832Srwatson u_long maxlabelstr; 55590150Smarkm int bcfile, maxflags; 55690150Smarkm gid_t maxgroup; 55790150Smarkm uid_t maxuser; 55890150Smarkm size_t flen, ulen, glen; 55937932Shoek char *initmax; 5601556Srgrimes int entries, needstats; 56196649Sjmallett const char *user, *group; 562105832Srwatson char *flags, *labelstr = NULL; 56350050Ssheldonh char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 56450050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 56550050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5661556Srgrimes 5671556Srgrimes needstats = f_inode || f_longform || f_size; 5681556Srgrimes flen = 0; 56937932Shoek btotal = 0; 57037932Shoek initmax = getenv("LS_COLWIDTHS"); 57137932Shoek /* Fields match -lios order. New ones should be added at the end. */ 572105832Srwatson maxlabelstr = maxblock = maxinode = maxlen = maxnlink = 57390150Smarkm maxuser = maxgroup = maxflags = maxsize = 0; 57437932Shoek if (initmax != NULL && *initmax != '\0') { 57537932Shoek char *initmax2, *jinitmax; 57637932Shoek int ninitmax; 57737932Shoek 57837932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 579114583Smarkm jinitmax = malloc(strlen(initmax) * 2 + 2); 58037932Shoek if (jinitmax == NULL) 58199744Sdillon err(1, "malloc"); 582114583Smarkm initmax2 = jinitmax; 58337932Shoek if (*initmax == ':') 58437932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 58537932Shoek else 58637932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 58737932Shoek for (initmax++; *initmax != '\0'; initmax++) { 58837932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 58937932Shoek *initmax2++ = '0'; 59037932Shoek *initmax2++ = initmax[0]; 59137932Shoek initmax2[1] = '\0'; 59237932Shoek } else { 59337932Shoek *initmax2++ = initmax[0]; 59437932Shoek initmax2[1] = '\0'; 59537932Shoek } 59637932Shoek } 59788602Sjoe if (initmax2[-1] == ':') 59888602Sjoe strcpy(initmax2, "0"); 59937932Shoek 60037932Shoek ninitmax = sscanf(jinitmax, 601114583Smarkm " %lu : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", 60237932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 603105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 60437932Shoek f_notabs = 1; 60537932Shoek switch (ninitmax) { 60688602Sjoe case 0: 60788602Sjoe maxinode = 0; 608102410Scharnier /* FALLTHROUGH */ 60988602Sjoe case 1: 61088602Sjoe maxblock = 0; 611102410Scharnier /* FALLTHROUGH */ 61288602Sjoe case 2: 61388602Sjoe maxnlink = 0; 614102410Scharnier /* FALLTHROUGH */ 61588602Sjoe case 3: 61688602Sjoe maxuser = 0; 617102410Scharnier /* FALLTHROUGH */ 61888602Sjoe case 4: 61988602Sjoe maxgroup = 0; 620102410Scharnier /* FALLTHROUGH */ 62188602Sjoe case 5: 62288602Sjoe maxflags = 0; 623102410Scharnier /* FALLTHROUGH */ 62488602Sjoe case 6: 62588602Sjoe maxsize = 0; 626102410Scharnier /* FALLTHROUGH */ 62788602Sjoe case 7: 62888602Sjoe maxlen = 0; 629102410Scharnier /* FALLTHROUGH */ 63088602Sjoe case 8: 631105832Srwatson maxlabelstr = 0; 632102410Scharnier /* FALLTHROUGH */ 63361338Sache#ifdef COLORLS 63488602Sjoe if (!f_color) 63561337Sache#endif 63688602Sjoe f_notabs = 0; 637102410Scharnier /* FALLTHROUGH */ 63890150Smarkm default: 63996681Sbillf break; 64037932Shoek } 641114583Smarkm MAKENINES(maxinode); 642114583Smarkm MAKENINES(maxblock); 643114583Smarkm MAKENINES(maxnlink); 644114583Smarkm MAKENINES(maxsize); 645130029Sle free(jinitmax); 64690150Smarkm } 6471556Srgrimes bcfile = 0; 6487165Sjoerg flags = NULL; 6491556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6501556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6511556Srgrimes warnx("%s: %s", 6521556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6531556Srgrimes cur->fts_number = NO_PRINT; 65417852Sadam rval = 1; 6551556Srgrimes continue; 6561556Srgrimes } 6571556Srgrimes /* 6581556Srgrimes * P is NULL if list is the argv list, to which different rules 6591556Srgrimes * apply. 6601556Srgrimes */ 6611556Srgrimes if (p == NULL) { 6621556Srgrimes /* Directories will be displayed later. */ 6631556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6641556Srgrimes cur->fts_number = NO_PRINT; 6651556Srgrimes continue; 6661556Srgrimes } 6671556Srgrimes } else { 6681556Srgrimes /* Only display dot file if -a/-A set. */ 669152469Sru if (cur->fts_name[0] == '.' && !f_listdot) { 6701556Srgrimes cur->fts_number = NO_PRINT; 6711556Srgrimes continue; 6721556Srgrimes } 6731556Srgrimes } 6741556Srgrimes if (cur->fts_namelen > maxlen) 6751556Srgrimes maxlen = cur->fts_namelen; 67635417Sdes if (f_octal || f_octal_escape) { 67788602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 67888602Sjoe 67988602Sjoe if (t > maxlen) 68088602Sjoe maxlen = t; 68137932Shoek } 6821556Srgrimes if (needstats) { 6831556Srgrimes sp = cur->fts_statp; 6841556Srgrimes if (sp->st_blocks > maxblock) 6851556Srgrimes maxblock = sp->st_blocks; 6861556Srgrimes if (sp->st_ino > maxinode) 6871556Srgrimes maxinode = sp->st_ino; 6881556Srgrimes if (sp->st_nlink > maxnlink) 6891556Srgrimes maxnlink = sp->st_nlink; 6901556Srgrimes if (sp->st_size > maxsize) 6911556Srgrimes maxsize = sp->st_size; 6921556Srgrimes 6931556Srgrimes btotal += sp->st_blocks; 6941556Srgrimes if (f_longform) { 69549373Ssheldonh if (f_numericonly) { 69649373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 69749373Ssheldonh "%u", sp->st_uid); 69849373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 69949373Ssheldonh "%u", sp->st_gid); 70049373Ssheldonh user = nuser; 70149373Ssheldonh group = ngroup; 70249373Ssheldonh } else { 70349373Ssheldonh user = user_from_uid(sp->st_uid, 0); 70449373Ssheldonh group = group_from_gid(sp->st_gid, 0); 70549373Ssheldonh } 7061556Srgrimes if ((ulen = strlen(user)) > maxuser) 7071556Srgrimes maxuser = ulen; 7081556Srgrimes if ((glen = strlen(group)) > maxgroup) 7091556Srgrimes maxgroup = glen; 7101556Srgrimes if (f_flags) { 71161749Sjoe flags = fflagstostr(sp->st_flags); 71261749Sjoe if (flags != NULL && *flags == '\0') { 71361749Sjoe free(flags); 71461749Sjoe flags = strdup("-"); 71561749Sjoe } 71661749Sjoe if (flags == NULL) 71799744Sdillon err(1, "fflagstostr"); 71890150Smarkm flen = strlen(flags); 71990150Smarkm if (flen > (size_t)maxflags) 7201556Srgrimes maxflags = flen; 7211556Srgrimes } else 7221556Srgrimes flen = 0; 723105832Srwatson labelstr = NULL; 724105832Srwatson if (f_label) { 725105836Srwatson char name[PATH_MAX + 1]; 726105832Srwatson mac_t label; 727105832Srwatson int error; 728105832Srwatson 729105832Srwatson error = mac_prepare_file_label(&label); 730105832Srwatson if (error == -1) { 731108057Srwatson warn("MAC label for %s/%s", 732108057Srwatson cur->fts_parent->fts_path, 733108057Srwatson cur->fts_name); 734105832Srwatson goto label_out; 735105832Srwatson } 736105832Srwatson 737105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 738105836Srwatson snprintf(name, sizeof(name), 739105836Srwatson "%s", cur->fts_name); 740105836Srwatson else 741105836Srwatson snprintf(name, sizeof(name), 742108057Srwatson "%s/%s", cur->fts_parent-> 743108057Srwatson fts_accpath, cur->fts_name); 744105836Srwatson 745105832Srwatson if (options & FTS_LOGICAL) 746105836Srwatson error = mac_get_file(name, 747105836Srwatson label); 748105832Srwatson else 749105836Srwatson error = mac_get_link(name, 750105836Srwatson label); 751105832Srwatson if (error == -1) { 752108057Srwatson warn("MAC label for %s/%s", 753108057Srwatson cur->fts_parent->fts_path, 754108057Srwatson cur->fts_name); 755105832Srwatson mac_free(label); 756105832Srwatson goto label_out; 757105832Srwatson } 758105832Srwatson 759105832Srwatson error = mac_to_text(label, 760105832Srwatson &labelstr); 761105832Srwatson if (error == -1) { 762108057Srwatson warn("MAC label for %s/%s", 763108057Srwatson cur->fts_parent->fts_path, 764108057Srwatson cur->fts_name); 765105832Srwatson mac_free(label); 766105832Srwatson goto label_out; 767105832Srwatson } 768105832Srwatson mac_free(label); 769105832Srwatsonlabel_out: 770105832Srwatson if (labelstr == NULL) 771114047Srwatson labelstr = strdup("-"); 772105832Srwatson labelstrlen = strlen(labelstr); 773105832Srwatson if (labelstrlen > maxlabelstr) 774105832Srwatson maxlabelstr = labelstrlen; 77586922Sgreen } else 776105832Srwatson labelstrlen = 0; 7771556Srgrimes 778105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 77986922Sgreen ulen + glen + flen + 4)) == NULL) 78099744Sdillon err(1, "malloc"); 7811556Srgrimes 7821556Srgrimes np->user = &np->data[0]; 7831556Srgrimes (void)strcpy(np->user, user); 7841556Srgrimes np->group = &np->data[ulen + 1]; 7851556Srgrimes (void)strcpy(np->group, group); 7861556Srgrimes 7871556Srgrimes if (S_ISCHR(sp->st_mode) || 7881556Srgrimes S_ISBLK(sp->st_mode)) 7891556Srgrimes bcfile = 1; 7901556Srgrimes 7911556Srgrimes if (f_flags) { 7921556Srgrimes np->flags = &np->data[ulen + glen + 2]; 79388602Sjoe (void)strcpy(np->flags, flags); 79461749Sjoe free(flags); 7951556Srgrimes } 796105832Srwatson if (f_label) { 797105832Srwatson np->label = &np->data[ulen + glen + 2 79886922Sgreen + (f_flags ? flen + 1 : 0)]; 799105832Srwatson (void)strcpy(np->label, labelstr); 800105832Srwatson free(labelstr); 80186922Sgreen } 8021556Srgrimes cur->fts_pointer = np; 8031556Srgrimes } 8041556Srgrimes } 8051556Srgrimes ++entries; 8061556Srgrimes } 8071556Srgrimes 808130237Sdas /* 809130237Sdas * If there are no entries to display, we normally stop right 810130237Sdas * here. However, we must continue if we have to display the 811130237Sdas * total block count. In this case, we display the total only 812130237Sdas * on the second (p != NULL) pass. 813130237Sdas */ 814130237Sdas if (!entries && (!(f_longform || f_size) || p == NULL)) 8151556Srgrimes return; 8161556Srgrimes 8171556Srgrimes d.list = list; 8181556Srgrimes d.entries = entries; 8191556Srgrimes d.maxlen = maxlen; 8201556Srgrimes if (needstats) { 8211556Srgrimes d.bcfile = bcfile; 8221556Srgrimes d.btotal = btotal; 8231556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 8241556Srgrimes d.s_block = strlen(buf); 8251556Srgrimes d.s_flags = maxflags; 826105832Srwatson d.s_label = maxlabelstr; 8271556Srgrimes d.s_group = maxgroup; 8281556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 8291556Srgrimes d.s_inode = strlen(buf); 8301556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 8311556Srgrimes d.s_nlink = strlen(buf); 832114583Smarkm (void)snprintf(buf, sizeof(buf), "%ju", maxsize); 8331556Srgrimes d.s_size = strlen(buf); 8341556Srgrimes d.s_user = maxuser; 8351556Srgrimes } 8361556Srgrimes printfcn(&d); 8371556Srgrimes output = 1; 8381556Srgrimes 8391556Srgrimes if (f_longform) 8401556Srgrimes for (cur = list; cur; cur = cur->fts_link) 8411556Srgrimes free(cur->fts_pointer); 8421556Srgrimes} 8431556Srgrimes 8441556Srgrimes/* 8451556Srgrimes * Ordering for mastercmp: 8461556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 8471556Srgrimes * as larger than directories. Within either group, use the sort function. 8481556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8491556Srgrimes */ 8501556Srgrimesstatic int 851103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8521556Srgrimes{ 8531556Srgrimes int a_info, b_info; 8541556Srgrimes 8551556Srgrimes a_info = (*a)->fts_info; 8561556Srgrimes if (a_info == FTS_ERR) 8571556Srgrimes return (0); 8581556Srgrimes b_info = (*b)->fts_info; 8591556Srgrimes if (b_info == FTS_ERR) 8601556Srgrimes return (0); 8611556Srgrimes 8621556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8631556Srgrimes return (namecmp(*a, *b)); 8641556Srgrimes 86529560Ssef if (a_info != b_info && 86629560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8671556Srgrimes if (a_info == FTS_D) 8681556Srgrimes return (1); 86929560Ssef if (b_info == FTS_D) 8701556Srgrimes return (-1); 87129560Ssef } 87229560Ssef return (sortfcn(*a, *b)); 8731556Srgrimes} 874