ls.c revision 130237
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 * 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 130237 2004-06-08 09:30:10Z das $"); 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 */ 10790150Smarkm int f_flags; /* show flags associated with a file */ 10890150Smarkm int f_humanval; /* show human-readable file sizes */ 10990150Smarkm int f_inode; /* print inode */ 11090150Smarkmstatic int f_kblocks; /* print size in kilobytes */ 11190150Smarkmstatic int f_listdir; /* list actual directory, not contents */ 11290150Smarkmstatic int f_listdot; /* list files beginning with . */ 11390150Smarkm int f_longform; /* long listing format */ 11490150Smarkm int f_nonprint; /* show unprintables as ? */ 11590150Smarkmstatic int f_nosort; /* don't sort output */ 11690150Smarkm int f_notabs; /* don't use tab-separated multi-col output */ 11790150Smarkmstatic int f_numericonly; /* don't convert uid/gid to name */ 11890150Smarkm int f_octal; /* show unprintables as \xxx */ 11990150Smarkm int f_octal_escape; /* like f_octal but use C escapes if possible */ 12090150Smarkmstatic int f_recursive; /* ls subdirectories also */ 12190150Smarkmstatic int f_reversesort; /* reverse whatever sort is used */ 12290150Smarkm int f_sectime; /* print the real time for all files */ 12390150Smarkmstatic int f_singlecol; /* use single column output */ 12490150Smarkm int f_size; /* list size in short listing */ 12596892Stjr int f_slash; /* similar to f_type, but only for dirs */ 12696892Stjr int f_sortacross; /* sort across rows, not down columns */ 12790150Smarkm int f_statustime; /* use time of last mode change */ 128114583Smarkmstatic int f_stream; /* stream the output, separate with commas */ 12990150Smarkmstatic int f_timesort; /* sort by time vice name */ 13090150Smarkm int f_type; /* add type character for non-regular files */ 13190150Smarkmstatic int f_whiteout; /* show whiteout entries */ 132105832Srwatson int f_label; /* show MAC label */ 13361268Sjoe#ifdef COLORLS 13490150Smarkm int f_color; /* add type in color for non-regular files */ 13561271Sjoe 13661271Sjoechar *ansi_bgcol; /* ANSI sequence to set background colour */ 13761271Sjoechar *ansi_fgcol; /* ANSI sequence to set foreground colour */ 13861271Sjoechar *ansi_coloff; /* ANSI sequence to reset colours */ 13988583Sjoechar *attrs_off; /* ANSI sequence to turn off attributes */ 14088583Sjoechar *enter_bold; /* ANSI sequence to set color to bold mode */ 14161268Sjoe#endif 1421556Srgrimes 14390150Smarkmstatic int rval; 14417852Sadam 1451556Srgrimesint 14690110Simpmain(int argc, char *argv[]) 1471556Srgrimes{ 14888602Sjoe static char dot[] = ".", *dotav[] = {dot, NULL}; 1491556Srgrimes struct winsize win; 1501556Srgrimes int ch, fts_options, notused; 1511556Srgrimes char *p; 15261271Sjoe#ifdef COLORLS 15388602Sjoe char termcapbuf[1024]; /* termcap definition buffer */ 15488602Sjoe char tcapbuf[512]; /* capability buffer */ 15561271Sjoe char *bp = tcapbuf; 15661271Sjoe#endif 15761271Sjoe 15888602Sjoe (void)setlocale(LC_ALL, ""); 15911808Sache 1601556Srgrimes /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 1611556Srgrimes if (isatty(STDOUT_FILENO)) { 16297803Stjr termwidth = 80; 16397803Stjr if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 16497803Stjr termwidth = atoi(p); 16597803Stjr else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 16697803Stjr win.ws_col > 0) 1671556Srgrimes termwidth = win.ws_col; 16890150Smarkm f_nonprint = 1; 1695158Sjoerg } else { 1701556Srgrimes f_singlecol = 1; 1715158Sjoerg /* retrieve environment variable, in case of explicit -C */ 17290150Smarkm p = getenv("COLUMNS"); 17390150Smarkm if (p) 1745158Sjoerg termwidth = atoi(p); 1755158Sjoerg } 1761556Srgrimes 1771556Srgrimes /* Root is -A automatically. */ 1781556Srgrimes if (!getuid()) 1791556Srgrimes f_listdot = 1; 1801556Srgrimes 1811556Srgrimes fts_options = FTS_PHYSICAL; 18296892Stjr while ((ch = getopt(argc, argv, "1ABCFGHLPRTWZabcdfghiklmnopqrstuwx")) 18396892Stjr != -1) { 1841556Srgrimes switch (ch) { 1851556Srgrimes /* 18696892Stjr * The -1, -C, -x and -l options all override each other so 18796892Stjr * shell aliasing works right. 1881556Srgrimes */ 1891556Srgrimes case '1': 1901556Srgrimes f_singlecol = 1; 19190150Smarkm f_longform = 0; 19296892Stjr f_stream = 0; 1931556Srgrimes break; 19435417Sdes case 'B': 19535417Sdes f_nonprint = 0; 19635417Sdes f_octal = 1; 19788602Sjoe f_octal_escape = 0; 19835417Sdes break; 1991556Srgrimes case 'C': 20096892Stjr f_sortacross = f_longform = f_singlecol = 0; 2011556Srgrimes break; 2021556Srgrimes case 'l': 2031556Srgrimes f_longform = 1; 20490150Smarkm f_singlecol = 0; 20596892Stjr f_stream = 0; 2061556Srgrimes break; 20796892Stjr case 'x': 20896892Stjr f_sortacross = 1; 20996892Stjr f_longform = 0; 21096892Stjr f_singlecol = 0; 21196892Stjr break; 2121556Srgrimes /* The -c and -u options override each other. */ 2131556Srgrimes case 'c': 2141556Srgrimes f_statustime = 1; 2151556Srgrimes f_accesstime = 0; 2161556Srgrimes break; 2171556Srgrimes case 'u': 2181556Srgrimes f_accesstime = 1; 2191556Srgrimes f_statustime = 0; 2201556Srgrimes break; 2211556Srgrimes case 'F': 2221556Srgrimes f_type = 1; 22396892Stjr f_slash = 0; 2241556Srgrimes break; 22535426Sdes case 'H': 22688602Sjoe fts_options |= FTS_COMFOLLOW; 22735426Sdes break; 22861178Sjoe case 'G': 22964568Sjoe setenv("CLICOLOR", "", 1); 23061178Sjoe break; 2311556Srgrimes case 'L': 2321556Srgrimes fts_options &= ~FTS_PHYSICAL; 2331556Srgrimes fts_options |= FTS_LOGICAL; 2341556Srgrimes break; 23535426Sdes case 'P': 23688602Sjoe fts_options &= ~FTS_COMFOLLOW; 23735426Sdes fts_options &= ~FTS_LOGICAL; 23835426Sdes fts_options |= FTS_PHYSICAL; 23935426Sdes break; 2401556Srgrimes case 'R': 2411556Srgrimes f_recursive = 1; 2421556Srgrimes break; 2431556Srgrimes case 'a': 2441556Srgrimes fts_options |= FTS_SEEDOT; 2451556Srgrimes /* FALLTHROUGH */ 2461556Srgrimes case 'A': 2471556Srgrimes f_listdot = 1; 2481556Srgrimes break; 2491556Srgrimes /* The -d option turns off the -R option. */ 2501556Srgrimes case 'd': 2511556Srgrimes f_listdir = 1; 2521556Srgrimes f_recursive = 0; 2531556Srgrimes break; 2541556Srgrimes case 'f': 2551556Srgrimes f_nosort = 1; 2561556Srgrimes break; 25788602Sjoe case 'g': /* Compatibility with 4.3BSD. */ 2581556Srgrimes break; 25988591Sjoe case 'h': 26088591Sjoe f_humanval = 1; 26188591Sjoe break; 2621556Srgrimes case 'i': 2631556Srgrimes f_inode = 1; 2641556Srgrimes break; 2652889Spst case 'k': 266123089Sobrien f_humanval = 0; 2672889Spst f_kblocks = 1; 2682889Spst break; 26996892Stjr case 'm': 27096892Stjr f_stream = 1; 27196892Stjr f_singlecol = 0; 27296892Stjr f_longform = 0; 27396892Stjr break; 27449373Ssheldonh case 'n': 27549373Ssheldonh f_numericonly = 1; 27649373Ssheldonh break; 2771556Srgrimes case 'o': 2781556Srgrimes f_flags = 1; 2791556Srgrimes break; 28096892Stjr case 'p': 28196892Stjr f_slash = 1; 28296892Stjr f_type = 1; 28396892Stjr break; 2841556Srgrimes case 'q': 2851556Srgrimes f_nonprint = 1; 28635373Sdes f_octal = 0; 28788602Sjoe f_octal_escape = 0; 2881556Srgrimes break; 2891556Srgrimes case 'r': 2901556Srgrimes f_reversesort = 1; 2911556Srgrimes break; 2921556Srgrimes case 's': 2931556Srgrimes f_size = 1; 2941556Srgrimes break; 2951556Srgrimes case 'T': 2961556Srgrimes f_sectime = 1; 2971556Srgrimes break; 2981556Srgrimes case 't': 2991556Srgrimes f_timesort = 1; 3001556Srgrimes break; 30120417Ssteve case 'W': 30220417Ssteve f_whiteout = 1; 30320417Ssteve break; 30435373Sdes case 'b': 30535373Sdes f_nonprint = 0; 30688602Sjoe f_octal = 0; 30735417Sdes f_octal_escape = 1; 30835373Sdes break; 30988603Sjoe case 'w': 31088603Sjoe f_nonprint = 0; 31188603Sjoe f_octal = 0; 31288603Sjoe f_octal_escape = 0; 31388603Sjoe break; 31486922Sgreen case 'Z': 315105832Srwatson f_label = 1; 31686922Sgreen break; 3171556Srgrimes default: 3181556Srgrimes case '?': 3191556Srgrimes usage(); 3201556Srgrimes } 3211556Srgrimes } 3221556Srgrimes argc -= optind; 3231556Srgrimes argv += optind; 3241556Srgrimes 32564568Sjoe /* Enabling of colours is conditional on the environment. */ 32664568Sjoe if (getenv("CLICOLOR") && 32764568Sjoe (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 32864604Sjoe#ifdef COLORLS 32964568Sjoe if (tgetent(termcapbuf, getenv("TERM")) == 1) { 33064568Sjoe ansi_fgcol = tgetstr("AF", &bp); 33164568Sjoe ansi_bgcol = tgetstr("AB", &bp); 33288583Sjoe attrs_off = tgetstr("me", &bp); 33388583Sjoe enter_bold = tgetstr("md", &bp); 33464568Sjoe 33564568Sjoe /* To switch colours off use 'op' if 33664568Sjoe * available, otherwise use 'oc', or 33764568Sjoe * don't do colours at all. */ 33864568Sjoe ansi_coloff = tgetstr("op", &bp); 33964568Sjoe if (!ansi_coloff) 34064568Sjoe ansi_coloff = tgetstr("oc", &bp); 34164568Sjoe if (ansi_fgcol && ansi_bgcol && ansi_coloff) 34264568Sjoe f_color = 1; 34364568Sjoe } 34464604Sjoe#else 345106479Stjr warnx("color support not compiled in"); 34664604Sjoe#endif /*COLORLS*/ 34764568Sjoe 34864604Sjoe#ifdef COLORLS 34961289Sache if (f_color) { 35061337Sache /* 35161337Sache * We can't put tabs and color sequences together: 35261337Sache * column number will be incremented incorrectly 35361337Sache * for "stty oxtabs" mode. 35461337Sache */ 35561337Sache f_notabs = 1; 35688602Sjoe (void)signal(SIGINT, colorquit); 35788602Sjoe (void)signal(SIGQUIT, colorquit); 35861178Sjoe parsecolors(getenv("LSCOLORS")); 35961289Sache } 36061268Sjoe#endif 36161178Sjoe 3621556Srgrimes /* 3631556Srgrimes * If not -F, -i, -l, -s or -t options, don't require stat 36461178Sjoe * information, unless in color mode in which case we do 36561178Sjoe * need this to determine which colors to display. 3661556Srgrimes */ 36761178Sjoe if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type 36861268Sjoe#ifdef COLORLS 36961268Sjoe && !f_color 37061268Sjoe#endif 37188602Sjoe ) 3721556Srgrimes fts_options |= FTS_NOSTAT; 3731556Srgrimes 3741556Srgrimes /* 3751556Srgrimes * If not -F, -d or -l options, follow any symbolic links listed on 3761556Srgrimes * the command line. 3771556Srgrimes */ 3781556Srgrimes if (!f_longform && !f_listdir && !f_type) 3791556Srgrimes fts_options |= FTS_COMFOLLOW; 3801556Srgrimes 38120417Ssteve /* 38220417Ssteve * If -W, show whiteout entries 38320417Ssteve */ 38420417Ssteve#ifdef FTS_WHITEOUT 38520417Ssteve if (f_whiteout) 38620417Ssteve fts_options |= FTS_WHITEOUT; 38720417Ssteve#endif 38820417Ssteve 3891556Srgrimes /* If -l or -s, figure out block size. */ 3901556Srgrimes if (f_longform || f_size) { 3912889Spst if (f_kblocks) 3927282Sphk blocksize = 2; 3937282Sphk else { 3947282Sphk (void)getbsize(¬used, &blocksize); 3957282Sphk blocksize /= 512; 3967282Sphk } 3971556Srgrimes } 3981556Srgrimes /* Select a sort function. */ 3991556Srgrimes if (f_reversesort) { 4001556Srgrimes if (!f_timesort) 4011556Srgrimes sortfcn = revnamecmp; 4021556Srgrimes else if (f_accesstime) 4031556Srgrimes sortfcn = revacccmp; 4041556Srgrimes else if (f_statustime) 4051556Srgrimes sortfcn = revstatcmp; 40688602Sjoe else /* Use modification time. */ 4071556Srgrimes sortfcn = revmodcmp; 4081556Srgrimes } else { 4091556Srgrimes if (!f_timesort) 4101556Srgrimes sortfcn = namecmp; 4111556Srgrimes else if (f_accesstime) 4121556Srgrimes sortfcn = acccmp; 4131556Srgrimes else if (f_statustime) 4141556Srgrimes sortfcn = statcmp; 41588602Sjoe else /* Use modification time. */ 4161556Srgrimes sortfcn = modcmp; 4171556Srgrimes } 4181556Srgrimes 4191556Srgrimes /* Select a print function. */ 4201556Srgrimes if (f_singlecol) 4211556Srgrimes printfcn = printscol; 4221556Srgrimes else if (f_longform) 4231556Srgrimes printfcn = printlong; 42496892Stjr else if (f_stream) 42596892Stjr printfcn = printstream; 4261556Srgrimes else 4271556Srgrimes printfcn = printcol; 4281556Srgrimes 4291556Srgrimes if (argc) 4301556Srgrimes traverse(argc, argv, fts_options); 4311556Srgrimes else 4321556Srgrimes traverse(1, dotav, fts_options); 43317852Sadam exit(rval); 4341556Srgrimes} 4351556Srgrimes 43688602Sjoestatic int output; /* If anything output. */ 4371556Srgrimes 4381556Srgrimes/* 4391556Srgrimes * Traverse() walks the logical directory structure specified by the argv list 4401556Srgrimes * in the order specified by the mastercmp() comparison function. During the 4411556Srgrimes * traversal it passes linked lists of structures to display() which represent 4421556Srgrimes * a superset (may be exact set) of the files to be displayed. 4431556Srgrimes */ 4441556Srgrimesstatic void 44590110Simptraverse(int argc, char *argv[], int options) 4461556Srgrimes{ 4471556Srgrimes FTS *ftsp; 4481556Srgrimes FTSENT *p, *chp; 4491556Srgrimes int ch_options; 4501556Srgrimes 4511556Srgrimes if ((ftsp = 4521556Srgrimes fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 45399744Sdillon err(1, "fts_open"); 4541556Srgrimes 455130237Sdas /* 456130237Sdas * We ignore errors from fts_children here since they will be 457130237Sdas * replicated and signalled on the next call to fts_read() below. 458130237Sdas */ 459130237Sdas chp = fts_children(ftsp, 0); 460130237Sdas if (chp != NULL) 461130237Sdas display(NULL, chp, options); 4621556Srgrimes if (f_listdir) 4631556Srgrimes return; 4641556Srgrimes 4651556Srgrimes /* 4661556Srgrimes * If not recursing down this tree and don't need stat info, just get 4671556Srgrimes * the names. 4681556Srgrimes */ 469108057Srwatson ch_options = !f_recursive && !f_label && 470108057Srwatson options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 4711556Srgrimes 4721556Srgrimes while ((p = fts_read(ftsp)) != NULL) 4731556Srgrimes switch (p->fts_info) { 4741556Srgrimes case FTS_DC: 4751556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 4761556Srgrimes break; 4771556Srgrimes case FTS_DNR: 4781556Srgrimes case FTS_ERR: 4791556Srgrimes warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 48017852Sadam rval = 1; 4811556Srgrimes break; 4821556Srgrimes case FTS_D: 4831556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 4841556Srgrimes p->fts_name[0] == '.' && !f_listdot) 4851556Srgrimes break; 4861556Srgrimes 4871556Srgrimes /* 4881556Srgrimes * If already output something, put out a newline as 4891556Srgrimes * a separator. If multiple arguments, precede each 4901556Srgrimes * directory with its name. 4911556Srgrimes */ 492105390Stjr if (output) { 493105390Stjr putchar('\n'); 494114583Smarkm (void)printname(p->fts_path); 495105390Stjr puts(":"); 496105390Stjr } else if (argc > 1) { 497114583Smarkm (void)printname(p->fts_path); 498105390Stjr puts(":"); 4991556Srgrimes output = 1; 5001556Srgrimes } 5011556Srgrimes chp = fts_children(ftsp, ch_options); 502105832Srwatson display(p, chp, options); 5031556Srgrimes 5041556Srgrimes if (!f_recursive && chp != NULL) 5051556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 5061556Srgrimes break; 50796681Sbillf default: 50896681Sbillf break; 5091556Srgrimes } 5101556Srgrimes if (errno) 5111556Srgrimes err(1, "fts_read"); 5121556Srgrimes} 5131556Srgrimes 5141556Srgrimes/* 5151556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 5161556Srgrimes * along with any other necessary information to the print function. P 5171556Srgrimes * points to the parent directory of the display list. 5181556Srgrimes */ 5191556Srgrimesstatic void 520114583Smarkmdisplay(const FTSENT *p, FTSENT *list, int options) 5211556Srgrimes{ 5221556Srgrimes struct stat *sp; 5231556Srgrimes DISPLAY d; 5241556Srgrimes FTSENT *cur; 5251556Srgrimes NAMES *np; 52690150Smarkm off_t maxsize; 527114583Smarkm long maxblock; 528114583Smarkm u_long btotal, labelstrlen, maxinode, maxlen, maxnlink; 529105832Srwatson u_long maxlabelstr; 53090150Smarkm int bcfile, maxflags; 53190150Smarkm gid_t maxgroup; 53290150Smarkm uid_t maxuser; 53390150Smarkm size_t flen, ulen, glen; 53437932Shoek char *initmax; 5351556Srgrimes int entries, needstats; 53696649Sjmallett const char *user, *group; 537105832Srwatson char *flags, *labelstr = NULL; 53850050Ssheldonh char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 53950050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 54050050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5411556Srgrimes 5421556Srgrimes needstats = f_inode || f_longform || f_size; 5431556Srgrimes flen = 0; 54437932Shoek btotal = 0; 54537932Shoek initmax = getenv("LS_COLWIDTHS"); 54637932Shoek /* Fields match -lios order. New ones should be added at the end. */ 547105832Srwatson maxlabelstr = maxblock = maxinode = maxlen = maxnlink = 54890150Smarkm maxuser = maxgroup = maxflags = maxsize = 0; 54937932Shoek if (initmax != NULL && *initmax != '\0') { 55037932Shoek char *initmax2, *jinitmax; 55137932Shoek int ninitmax; 55237932Shoek 55337932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 554114583Smarkm jinitmax = malloc(strlen(initmax) * 2 + 2); 55537932Shoek if (jinitmax == NULL) 55699744Sdillon err(1, "malloc"); 557114583Smarkm initmax2 = jinitmax; 55837932Shoek if (*initmax == ':') 55937932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 56037932Shoek else 56137932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 56237932Shoek for (initmax++; *initmax != '\0'; initmax++) { 56337932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 56437932Shoek *initmax2++ = '0'; 56537932Shoek *initmax2++ = initmax[0]; 56637932Shoek initmax2[1] = '\0'; 56737932Shoek } else { 56837932Shoek *initmax2++ = initmax[0]; 56937932Shoek initmax2[1] = '\0'; 57037932Shoek } 57137932Shoek } 57288602Sjoe if (initmax2[-1] == ':') 57388602Sjoe strcpy(initmax2, "0"); 57437932Shoek 57537932Shoek ninitmax = sscanf(jinitmax, 576114583Smarkm " %lu : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", 57737932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 578105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 57937932Shoek f_notabs = 1; 58037932Shoek switch (ninitmax) { 58188602Sjoe case 0: 58288602Sjoe maxinode = 0; 583102410Scharnier /* FALLTHROUGH */ 58488602Sjoe case 1: 58588602Sjoe maxblock = 0; 586102410Scharnier /* FALLTHROUGH */ 58788602Sjoe case 2: 58888602Sjoe maxnlink = 0; 589102410Scharnier /* FALLTHROUGH */ 59088602Sjoe case 3: 59188602Sjoe maxuser = 0; 592102410Scharnier /* FALLTHROUGH */ 59388602Sjoe case 4: 59488602Sjoe maxgroup = 0; 595102410Scharnier /* FALLTHROUGH */ 59688602Sjoe case 5: 59788602Sjoe maxflags = 0; 598102410Scharnier /* FALLTHROUGH */ 59988602Sjoe case 6: 60088602Sjoe maxsize = 0; 601102410Scharnier /* FALLTHROUGH */ 60288602Sjoe case 7: 60388602Sjoe maxlen = 0; 604102410Scharnier /* FALLTHROUGH */ 60588602Sjoe case 8: 606105832Srwatson maxlabelstr = 0; 607102410Scharnier /* FALLTHROUGH */ 60861338Sache#ifdef COLORLS 60988602Sjoe if (!f_color) 61061337Sache#endif 61188602Sjoe f_notabs = 0; 612102410Scharnier /* FALLTHROUGH */ 61390150Smarkm default: 61496681Sbillf break; 61537932Shoek } 616114583Smarkm MAKENINES(maxinode); 617114583Smarkm MAKENINES(maxblock); 618114583Smarkm MAKENINES(maxnlink); 619114583Smarkm MAKENINES(maxsize); 620130029Sle free(jinitmax); 62190150Smarkm } 6221556Srgrimes bcfile = 0; 6237165Sjoerg flags = NULL; 6241556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6251556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6261556Srgrimes warnx("%s: %s", 6271556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6281556Srgrimes cur->fts_number = NO_PRINT; 62917852Sadam rval = 1; 6301556Srgrimes continue; 6311556Srgrimes } 6321556Srgrimes /* 6331556Srgrimes * P is NULL if list is the argv list, to which different rules 6341556Srgrimes * apply. 6351556Srgrimes */ 6361556Srgrimes if (p == NULL) { 6371556Srgrimes /* Directories will be displayed later. */ 6381556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6391556Srgrimes cur->fts_number = NO_PRINT; 6401556Srgrimes continue; 6411556Srgrimes } 6421556Srgrimes } else { 6431556Srgrimes /* Only display dot file if -a/-A set. */ 6441556Srgrimes if (cur->fts_name[0] == '.' && !f_listdot) { 6451556Srgrimes cur->fts_number = NO_PRINT; 6461556Srgrimes continue; 6471556Srgrimes } 6481556Srgrimes } 6491556Srgrimes if (cur->fts_namelen > maxlen) 6501556Srgrimes maxlen = cur->fts_namelen; 65135417Sdes if (f_octal || f_octal_escape) { 65288602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 65388602Sjoe 65488602Sjoe if (t > maxlen) 65588602Sjoe maxlen = t; 65637932Shoek } 6571556Srgrimes if (needstats) { 6581556Srgrimes sp = cur->fts_statp; 6591556Srgrimes if (sp->st_blocks > maxblock) 6601556Srgrimes maxblock = sp->st_blocks; 6611556Srgrimes if (sp->st_ino > maxinode) 6621556Srgrimes maxinode = sp->st_ino; 6631556Srgrimes if (sp->st_nlink > maxnlink) 6641556Srgrimes maxnlink = sp->st_nlink; 6651556Srgrimes if (sp->st_size > maxsize) 6661556Srgrimes maxsize = sp->st_size; 6671556Srgrimes 6681556Srgrimes btotal += sp->st_blocks; 6691556Srgrimes if (f_longform) { 67049373Ssheldonh if (f_numericonly) { 67149373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 67249373Ssheldonh "%u", sp->st_uid); 67349373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 67449373Ssheldonh "%u", sp->st_gid); 67549373Ssheldonh user = nuser; 67649373Ssheldonh group = ngroup; 67749373Ssheldonh } else { 67849373Ssheldonh user = user_from_uid(sp->st_uid, 0); 67949373Ssheldonh group = group_from_gid(sp->st_gid, 0); 68049373Ssheldonh } 6811556Srgrimes if ((ulen = strlen(user)) > maxuser) 6821556Srgrimes maxuser = ulen; 6831556Srgrimes if ((glen = strlen(group)) > maxgroup) 6841556Srgrimes maxgroup = glen; 6851556Srgrimes if (f_flags) { 68661749Sjoe flags = fflagstostr(sp->st_flags); 68761749Sjoe if (flags != NULL && *flags == '\0') { 68861749Sjoe free(flags); 68961749Sjoe flags = strdup("-"); 69061749Sjoe } 69161749Sjoe if (flags == NULL) 69299744Sdillon err(1, "fflagstostr"); 69390150Smarkm flen = strlen(flags); 69490150Smarkm if (flen > (size_t)maxflags) 6951556Srgrimes maxflags = flen; 6961556Srgrimes } else 6971556Srgrimes flen = 0; 698105832Srwatson labelstr = NULL; 699105832Srwatson if (f_label) { 700105836Srwatson char name[PATH_MAX + 1]; 701105832Srwatson mac_t label; 702105832Srwatson int error; 703105832Srwatson 704105832Srwatson error = mac_prepare_file_label(&label); 705105832Srwatson if (error == -1) { 706108057Srwatson warn("MAC label for %s/%s", 707108057Srwatson cur->fts_parent->fts_path, 708108057Srwatson cur->fts_name); 709105832Srwatson goto label_out; 710105832Srwatson } 711105832Srwatson 712105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 713105836Srwatson snprintf(name, sizeof(name), 714105836Srwatson "%s", cur->fts_name); 715105836Srwatson else 716105836Srwatson snprintf(name, sizeof(name), 717108057Srwatson "%s/%s", cur->fts_parent-> 718108057Srwatson fts_accpath, cur->fts_name); 719105836Srwatson 720105832Srwatson if (options & FTS_LOGICAL) 721105836Srwatson error = mac_get_file(name, 722105836Srwatson label); 723105832Srwatson else 724105836Srwatson error = mac_get_link(name, 725105836Srwatson label); 726105832Srwatson if (error == -1) { 727108057Srwatson warn("MAC label for %s/%s", 728108057Srwatson cur->fts_parent->fts_path, 729108057Srwatson cur->fts_name); 730105832Srwatson mac_free(label); 731105832Srwatson goto label_out; 732105832Srwatson } 733105832Srwatson 734105832Srwatson error = mac_to_text(label, 735105832Srwatson &labelstr); 736105832Srwatson if (error == -1) { 737108057Srwatson warn("MAC label for %s/%s", 738108057Srwatson cur->fts_parent->fts_path, 739108057Srwatson cur->fts_name); 740105832Srwatson mac_free(label); 741105832Srwatson goto label_out; 742105832Srwatson } 743105832Srwatson mac_free(label); 744105832Srwatsonlabel_out: 745105832Srwatson if (labelstr == NULL) 746114047Srwatson labelstr = strdup("-"); 747105832Srwatson labelstrlen = strlen(labelstr); 748105832Srwatson if (labelstrlen > maxlabelstr) 749105832Srwatson maxlabelstr = labelstrlen; 75086922Sgreen } else 751105832Srwatson labelstrlen = 0; 7521556Srgrimes 753105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 75486922Sgreen ulen + glen + flen + 4)) == NULL) 75599744Sdillon err(1, "malloc"); 7561556Srgrimes 7571556Srgrimes np->user = &np->data[0]; 7581556Srgrimes (void)strcpy(np->user, user); 7591556Srgrimes np->group = &np->data[ulen + 1]; 7601556Srgrimes (void)strcpy(np->group, group); 7611556Srgrimes 7621556Srgrimes if (S_ISCHR(sp->st_mode) || 7631556Srgrimes S_ISBLK(sp->st_mode)) 7641556Srgrimes bcfile = 1; 7651556Srgrimes 7661556Srgrimes if (f_flags) { 7671556Srgrimes np->flags = &np->data[ulen + glen + 2]; 76888602Sjoe (void)strcpy(np->flags, flags); 76961749Sjoe free(flags); 7701556Srgrimes } 771105832Srwatson if (f_label) { 772105832Srwatson np->label = &np->data[ulen + glen + 2 77386922Sgreen + (f_flags ? flen + 1 : 0)]; 774105832Srwatson (void)strcpy(np->label, labelstr); 775105832Srwatson free(labelstr); 77686922Sgreen } 7771556Srgrimes cur->fts_pointer = np; 7781556Srgrimes } 7791556Srgrimes } 7801556Srgrimes ++entries; 7811556Srgrimes } 7821556Srgrimes 783130237Sdas /* 784130237Sdas * If there are no entries to display, we normally stop right 785130237Sdas * here. However, we must continue if we have to display the 786130237Sdas * total block count. In this case, we display the total only 787130237Sdas * on the second (p != NULL) pass. 788130237Sdas */ 789130237Sdas if (!entries && (!(f_longform || f_size) || p == NULL)) 7901556Srgrimes return; 7911556Srgrimes 7921556Srgrimes d.list = list; 7931556Srgrimes d.entries = entries; 7941556Srgrimes d.maxlen = maxlen; 7951556Srgrimes if (needstats) { 7961556Srgrimes d.bcfile = bcfile; 7971556Srgrimes d.btotal = btotal; 7981556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 7991556Srgrimes d.s_block = strlen(buf); 8001556Srgrimes d.s_flags = maxflags; 801105832Srwatson d.s_label = maxlabelstr; 8021556Srgrimes d.s_group = maxgroup; 8031556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 8041556Srgrimes d.s_inode = strlen(buf); 8051556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 8061556Srgrimes d.s_nlink = strlen(buf); 807114583Smarkm (void)snprintf(buf, sizeof(buf), "%ju", maxsize); 8081556Srgrimes d.s_size = strlen(buf); 8091556Srgrimes d.s_user = maxuser; 8101556Srgrimes } 8111556Srgrimes printfcn(&d); 8121556Srgrimes output = 1; 8131556Srgrimes 8141556Srgrimes if (f_longform) 8151556Srgrimes for (cur = list; cur; cur = cur->fts_link) 8161556Srgrimes free(cur->fts_pointer); 8171556Srgrimes} 8181556Srgrimes 8191556Srgrimes/* 8201556Srgrimes * Ordering for mastercmp: 8211556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 8221556Srgrimes * as larger than directories. Within either group, use the sort function. 8231556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8241556Srgrimes */ 8251556Srgrimesstatic int 826103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8271556Srgrimes{ 8281556Srgrimes int a_info, b_info; 8291556Srgrimes 8301556Srgrimes a_info = (*a)->fts_info; 8311556Srgrimes if (a_info == FTS_ERR) 8321556Srgrimes return (0); 8331556Srgrimes b_info = (*b)->fts_info; 8341556Srgrimes if (b_info == FTS_ERR) 8351556Srgrimes return (0); 8361556Srgrimes 8371556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8381556Srgrimes return (namecmp(*a, *b)); 8391556Srgrimes 84029560Ssef if (a_info != b_info && 84129560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8421556Srgrimes if (a_info == FTS_D) 8431556Srgrimes return (1); 84429560Ssef if (b_info == FTS_D) 8451556Srgrimes return (-1); 84629560Ssef } 84729560Ssef return (sortfcn(*a, *b)); 8481556Srgrimes} 849