ls.c revision 152256
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 152256 2005-11-10 00:02:32Z mux $"); 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 . */ 113152256Smuxstatic int f_nolistdot; /* don't list files beginning with . */ 114152256Smuxstatic int f_forcelistdot; /* force list files beginning with . */ 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, 182152256Smux "1ABCFGHILPRSTWZabcdfghiklmnopqrstuwx")) != -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; 2111556Srgrimes /* The -c and -u options override each other. */ 2121556Srgrimes case 'c': 2131556Srgrimes f_statustime = 1; 2141556Srgrimes f_accesstime = 0; 2151556Srgrimes break; 2161556Srgrimes case 'u': 2171556Srgrimes f_accesstime = 1; 2181556Srgrimes f_statustime = 0; 2191556Srgrimes break; 2201556Srgrimes case 'F': 2211556Srgrimes f_type = 1; 22296892Stjr f_slash = 0; 2231556Srgrimes break; 22435426Sdes case 'H': 22588602Sjoe fts_options |= FTS_COMFOLLOW; 22635426Sdes break; 22761178Sjoe case 'G': 22864568Sjoe setenv("CLICOLOR", "", 1); 22961178Sjoe break; 2301556Srgrimes case 'L': 2311556Srgrimes fts_options &= ~FTS_PHYSICAL; 2321556Srgrimes fts_options |= FTS_LOGICAL; 2331556Srgrimes break; 23435426Sdes case 'P': 23588602Sjoe fts_options &= ~FTS_COMFOLLOW; 23635426Sdes fts_options &= ~FTS_LOGICAL; 23735426Sdes fts_options |= FTS_PHYSICAL; 23835426Sdes break; 2391556Srgrimes case 'R': 2401556Srgrimes f_recursive = 1; 2411556Srgrimes break; 2421556Srgrimes case 'a': 2431556Srgrimes fts_options |= FTS_SEEDOT; 244152256Smux f_forcelistdot = 1; 245152256Smux break; 2461556Srgrimes case 'A': 2471556Srgrimes f_listdot = 1; 2481556Srgrimes break; 249152256Smux case 'I': 250152256Smux f_nolistdot = 1; 251152256Smux break; 2521556Srgrimes /* The -d option turns off the -R option. */ 2531556Srgrimes case 'd': 2541556Srgrimes f_listdir = 1; 2551556Srgrimes f_recursive = 0; 2561556Srgrimes break; 2571556Srgrimes case 'f': 2581556Srgrimes f_nosort = 1; 2591556Srgrimes break; 26088602Sjoe case 'g': /* Compatibility with 4.3BSD. */ 2611556Srgrimes break; 26288591Sjoe case 'h': 26388591Sjoe f_humanval = 1; 26488591Sjoe break; 2651556Srgrimes case 'i': 2661556Srgrimes f_inode = 1; 2671556Srgrimes break; 2682889Spst case 'k': 269123089Sobrien f_humanval = 0; 2702889Spst f_kblocks = 1; 2712889Spst break; 27296892Stjr case 'm': 27396892Stjr f_stream = 1; 27496892Stjr f_singlecol = 0; 27596892Stjr f_longform = 0; 27696892Stjr break; 27749373Ssheldonh case 'n': 27849373Ssheldonh f_numericonly = 1; 27949373Ssheldonh break; 2801556Srgrimes case 'o': 2811556Srgrimes f_flags = 1; 2821556Srgrimes break; 28396892Stjr case 'p': 28496892Stjr f_slash = 1; 28596892Stjr f_type = 1; 28696892Stjr break; 2871556Srgrimes case 'q': 2881556Srgrimes f_nonprint = 1; 28935373Sdes f_octal = 0; 29088602Sjoe f_octal_escape = 0; 2911556Srgrimes break; 2921556Srgrimes case 'r': 2931556Srgrimes f_reversesort = 1; 2941556Srgrimes break; 2951556Srgrimes case 's': 2961556Srgrimes f_size = 1; 2971556Srgrimes break; 2981556Srgrimes case 'T': 2991556Srgrimes f_sectime = 1; 3001556Srgrimes break; 3011556Srgrimes case 't': 3021556Srgrimes f_timesort = 1; 3031556Srgrimes break; 304146924Sdd case 'S': 305146924Sdd f_sizesort = 1; 306146924Sdd break; 30720417Ssteve case 'W': 30820417Ssteve f_whiteout = 1; 30920417Ssteve break; 31035373Sdes case 'b': 31135373Sdes f_nonprint = 0; 31288602Sjoe f_octal = 0; 31335417Sdes f_octal_escape = 1; 31435373Sdes break; 31588603Sjoe case 'w': 31688603Sjoe f_nonprint = 0; 31788603Sjoe f_octal = 0; 31888603Sjoe f_octal_escape = 0; 31988603Sjoe break; 32086922Sgreen case 'Z': 321105832Srwatson f_label = 1; 32286922Sgreen break; 3231556Srgrimes default: 3241556Srgrimes case '?': 3251556Srgrimes usage(); 3261556Srgrimes } 3271556Srgrimes } 3281556Srgrimes argc -= optind; 3291556Srgrimes argv += optind; 3301556Srgrimes 331152256Smux /* Root is -A automatically. */ 332152256Smux if (!getuid() && !f_nolistdot) 333152256Smux f_listdot = 1; 334152256Smux 33564568Sjoe /* Enabling of colours is conditional on the environment. */ 33664568Sjoe if (getenv("CLICOLOR") && 33764568Sjoe (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 33864604Sjoe#ifdef COLORLS 33964568Sjoe if (tgetent(termcapbuf, getenv("TERM")) == 1) { 34064568Sjoe ansi_fgcol = tgetstr("AF", &bp); 34164568Sjoe ansi_bgcol = tgetstr("AB", &bp); 34288583Sjoe attrs_off = tgetstr("me", &bp); 34388583Sjoe enter_bold = tgetstr("md", &bp); 34464568Sjoe 34564568Sjoe /* To switch colours off use 'op' if 34664568Sjoe * available, otherwise use 'oc', or 34764568Sjoe * don't do colours at all. */ 34864568Sjoe ansi_coloff = tgetstr("op", &bp); 34964568Sjoe if (!ansi_coloff) 35064568Sjoe ansi_coloff = tgetstr("oc", &bp); 35164568Sjoe if (ansi_fgcol && ansi_bgcol && ansi_coloff) 35264568Sjoe f_color = 1; 35364568Sjoe } 35464604Sjoe#else 355106479Stjr warnx("color support not compiled in"); 35664604Sjoe#endif /*COLORLS*/ 35764568Sjoe 35864604Sjoe#ifdef COLORLS 35961289Sache if (f_color) { 36061337Sache /* 36161337Sache * We can't put tabs and color sequences together: 36261337Sache * column number will be incremented incorrectly 36361337Sache * for "stty oxtabs" mode. 36461337Sache */ 36561337Sache f_notabs = 1; 36688602Sjoe (void)signal(SIGINT, colorquit); 36788602Sjoe (void)signal(SIGQUIT, colorquit); 36861178Sjoe parsecolors(getenv("LSCOLORS")); 36961289Sache } 37061268Sjoe#endif 37161178Sjoe 3721556Srgrimes /* 373146924Sdd * If not -F, -i, -l, -s, -S or -t options, don't require stat 37461178Sjoe * information, unless in color mode in which case we do 37561178Sjoe * need this to determine which colors to display. 3761556Srgrimes */ 377146924Sdd if (!f_inode && !f_longform && !f_size && !f_timesort && 378146924Sdd !f_sizesort && !f_type 37961268Sjoe#ifdef COLORLS 38061268Sjoe && !f_color 38161268Sjoe#endif 38288602Sjoe ) 3831556Srgrimes fts_options |= FTS_NOSTAT; 3841556Srgrimes 3851556Srgrimes /* 3861556Srgrimes * If not -F, -d or -l options, follow any symbolic links listed on 3871556Srgrimes * the command line. 3881556Srgrimes */ 3891556Srgrimes if (!f_longform && !f_listdir && !f_type) 3901556Srgrimes fts_options |= FTS_COMFOLLOW; 3911556Srgrimes 39220417Ssteve /* 39320417Ssteve * If -W, show whiteout entries 39420417Ssteve */ 39520417Ssteve#ifdef FTS_WHITEOUT 39620417Ssteve if (f_whiteout) 39720417Ssteve fts_options |= FTS_WHITEOUT; 39820417Ssteve#endif 39920417Ssteve 4001556Srgrimes /* If -l or -s, figure out block size. */ 4011556Srgrimes if (f_longform || f_size) { 4022889Spst if (f_kblocks) 4037282Sphk blocksize = 2; 4047282Sphk else { 4057282Sphk (void)getbsize(¬used, &blocksize); 4067282Sphk blocksize /= 512; 4077282Sphk } 4081556Srgrimes } 4091556Srgrimes /* Select a sort function. */ 4101556Srgrimes if (f_reversesort) { 411146924Sdd if (!f_timesort && !f_sizesort) 4121556Srgrimes sortfcn = revnamecmp; 4131556Srgrimes else if (f_accesstime) 4141556Srgrimes sortfcn = revacccmp; 4151556Srgrimes else if (f_statustime) 4161556Srgrimes sortfcn = revstatcmp; 417146924Sdd else if (f_sizesort) 418146924Sdd sortfcn = revsizecmp; 41988602Sjoe else /* Use modification time. */ 4201556Srgrimes sortfcn = revmodcmp; 4211556Srgrimes } else { 422146924Sdd if (!f_timesort && !f_sizesort) 4231556Srgrimes sortfcn = namecmp; 4241556Srgrimes else if (f_accesstime) 4251556Srgrimes sortfcn = acccmp; 4261556Srgrimes else if (f_statustime) 4271556Srgrimes sortfcn = statcmp; 428146924Sdd else if (f_sizesort) 429146924Sdd sortfcn = sizecmp; 43088602Sjoe else /* Use modification time. */ 4311556Srgrimes sortfcn = modcmp; 4321556Srgrimes } 4331556Srgrimes 4341556Srgrimes /* Select a print function. */ 4351556Srgrimes if (f_singlecol) 4361556Srgrimes printfcn = printscol; 4371556Srgrimes else if (f_longform) 4381556Srgrimes printfcn = printlong; 43996892Stjr else if (f_stream) 44096892Stjr printfcn = printstream; 4411556Srgrimes else 4421556Srgrimes printfcn = printcol; 4431556Srgrimes 4441556Srgrimes if (argc) 4451556Srgrimes traverse(argc, argv, fts_options); 4461556Srgrimes else 4471556Srgrimes traverse(1, dotav, fts_options); 44817852Sadam exit(rval); 4491556Srgrimes} 4501556Srgrimes 45188602Sjoestatic int output; /* If anything output. */ 4521556Srgrimes 4531556Srgrimes/* 4541556Srgrimes * Traverse() walks the logical directory structure specified by the argv list 4551556Srgrimes * in the order specified by the mastercmp() comparison function. During the 4561556Srgrimes * traversal it passes linked lists of structures to display() which represent 4571556Srgrimes * a superset (may be exact set) of the files to be displayed. 4581556Srgrimes */ 4591556Srgrimesstatic void 46090110Simptraverse(int argc, char *argv[], int options) 4611556Srgrimes{ 4621556Srgrimes FTS *ftsp; 4631556Srgrimes FTSENT *p, *chp; 4641556Srgrimes int ch_options; 4651556Srgrimes 4661556Srgrimes if ((ftsp = 4671556Srgrimes fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 46899744Sdillon err(1, "fts_open"); 4691556Srgrimes 470130237Sdas /* 471130237Sdas * We ignore errors from fts_children here since they will be 472130237Sdas * replicated and signalled on the next call to fts_read() below. 473130237Sdas */ 474130237Sdas chp = fts_children(ftsp, 0); 475130237Sdas if (chp != NULL) 476130237Sdas display(NULL, chp, options); 4771556Srgrimes if (f_listdir) 4781556Srgrimes return; 4791556Srgrimes 4801556Srgrimes /* 4811556Srgrimes * If not recursing down this tree and don't need stat info, just get 4821556Srgrimes * the names. 4831556Srgrimes */ 484108057Srwatson ch_options = !f_recursive && !f_label && 485108057Srwatson options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 4861556Srgrimes 4871556Srgrimes while ((p = fts_read(ftsp)) != NULL) 4881556Srgrimes switch (p->fts_info) { 4891556Srgrimes case FTS_DC: 4901556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 4911556Srgrimes break; 4921556Srgrimes case FTS_DNR: 4931556Srgrimes case FTS_ERR: 4941556Srgrimes warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 49517852Sadam rval = 1; 4961556Srgrimes break; 4971556Srgrimes case FTS_D: 4981556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 499152256Smux p->fts_name[0] == '.' && ((!f_listdot || 500152256Smux f_nolistdot) && !f_forcelistdot)) 5011556Srgrimes break; 5021556Srgrimes 5031556Srgrimes /* 5041556Srgrimes * If already output something, put out a newline as 5051556Srgrimes * a separator. If multiple arguments, precede each 5061556Srgrimes * directory with its name. 5071556Srgrimes */ 508105390Stjr if (output) { 509105390Stjr putchar('\n'); 510114583Smarkm (void)printname(p->fts_path); 511105390Stjr puts(":"); 512105390Stjr } else if (argc > 1) { 513114583Smarkm (void)printname(p->fts_path); 514105390Stjr puts(":"); 5151556Srgrimes output = 1; 5161556Srgrimes } 5171556Srgrimes chp = fts_children(ftsp, ch_options); 518105832Srwatson display(p, chp, options); 5191556Srgrimes 5201556Srgrimes if (!f_recursive && chp != NULL) 5211556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 5221556Srgrimes break; 52396681Sbillf default: 52496681Sbillf break; 5251556Srgrimes } 5261556Srgrimes if (errno) 5271556Srgrimes err(1, "fts_read"); 5281556Srgrimes} 5291556Srgrimes 5301556Srgrimes/* 5311556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 5321556Srgrimes * along with any other necessary information to the print function. P 5331556Srgrimes * points to the parent directory of the display list. 5341556Srgrimes */ 5351556Srgrimesstatic void 536114583Smarkmdisplay(const FTSENT *p, FTSENT *list, int options) 5371556Srgrimes{ 5381556Srgrimes struct stat *sp; 5391556Srgrimes DISPLAY d; 5401556Srgrimes FTSENT *cur; 5411556Srgrimes NAMES *np; 54290150Smarkm off_t maxsize; 543114583Smarkm long maxblock; 544114583Smarkm u_long btotal, labelstrlen, maxinode, maxlen, maxnlink; 545105832Srwatson u_long maxlabelstr; 54690150Smarkm int bcfile, maxflags; 54790150Smarkm gid_t maxgroup; 54890150Smarkm uid_t maxuser; 54990150Smarkm size_t flen, ulen, glen; 55037932Shoek char *initmax; 5511556Srgrimes int entries, needstats; 55296649Sjmallett const char *user, *group; 553105832Srwatson char *flags, *labelstr = NULL; 55450050Ssheldonh char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 55550050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 55650050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5571556Srgrimes 5581556Srgrimes needstats = f_inode || f_longform || f_size; 5591556Srgrimes flen = 0; 56037932Shoek btotal = 0; 56137932Shoek initmax = getenv("LS_COLWIDTHS"); 56237932Shoek /* Fields match -lios order. New ones should be added at the end. */ 563105832Srwatson maxlabelstr = maxblock = maxinode = maxlen = maxnlink = 56490150Smarkm maxuser = maxgroup = maxflags = maxsize = 0; 56537932Shoek if (initmax != NULL && *initmax != '\0') { 56637932Shoek char *initmax2, *jinitmax; 56737932Shoek int ninitmax; 56837932Shoek 56937932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 570114583Smarkm jinitmax = malloc(strlen(initmax) * 2 + 2); 57137932Shoek if (jinitmax == NULL) 57299744Sdillon err(1, "malloc"); 573114583Smarkm initmax2 = jinitmax; 57437932Shoek if (*initmax == ':') 57537932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 57637932Shoek else 57737932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 57837932Shoek for (initmax++; *initmax != '\0'; initmax++) { 57937932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 58037932Shoek *initmax2++ = '0'; 58137932Shoek *initmax2++ = initmax[0]; 58237932Shoek initmax2[1] = '\0'; 58337932Shoek } else { 58437932Shoek *initmax2++ = initmax[0]; 58537932Shoek initmax2[1] = '\0'; 58637932Shoek } 58737932Shoek } 58888602Sjoe if (initmax2[-1] == ':') 58988602Sjoe strcpy(initmax2, "0"); 59037932Shoek 59137932Shoek ninitmax = sscanf(jinitmax, 592114583Smarkm " %lu : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", 59337932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 594105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 59537932Shoek f_notabs = 1; 59637932Shoek switch (ninitmax) { 59788602Sjoe case 0: 59888602Sjoe maxinode = 0; 599102410Scharnier /* FALLTHROUGH */ 60088602Sjoe case 1: 60188602Sjoe maxblock = 0; 602102410Scharnier /* FALLTHROUGH */ 60388602Sjoe case 2: 60488602Sjoe maxnlink = 0; 605102410Scharnier /* FALLTHROUGH */ 60688602Sjoe case 3: 60788602Sjoe maxuser = 0; 608102410Scharnier /* FALLTHROUGH */ 60988602Sjoe case 4: 61088602Sjoe maxgroup = 0; 611102410Scharnier /* FALLTHROUGH */ 61288602Sjoe case 5: 61388602Sjoe maxflags = 0; 614102410Scharnier /* FALLTHROUGH */ 61588602Sjoe case 6: 61688602Sjoe maxsize = 0; 617102410Scharnier /* FALLTHROUGH */ 61888602Sjoe case 7: 61988602Sjoe maxlen = 0; 620102410Scharnier /* FALLTHROUGH */ 62188602Sjoe case 8: 622105832Srwatson maxlabelstr = 0; 623102410Scharnier /* FALLTHROUGH */ 62461338Sache#ifdef COLORLS 62588602Sjoe if (!f_color) 62661337Sache#endif 62788602Sjoe f_notabs = 0; 628102410Scharnier /* FALLTHROUGH */ 62990150Smarkm default: 63096681Sbillf break; 63137932Shoek } 632114583Smarkm MAKENINES(maxinode); 633114583Smarkm MAKENINES(maxblock); 634114583Smarkm MAKENINES(maxnlink); 635114583Smarkm MAKENINES(maxsize); 636130029Sle free(jinitmax); 63790150Smarkm } 6381556Srgrimes bcfile = 0; 6397165Sjoerg flags = NULL; 6401556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6411556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6421556Srgrimes warnx("%s: %s", 6431556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6441556Srgrimes cur->fts_number = NO_PRINT; 64517852Sadam rval = 1; 6461556Srgrimes continue; 6471556Srgrimes } 6481556Srgrimes /* 6491556Srgrimes * P is NULL if list is the argv list, to which different rules 6501556Srgrimes * apply. 6511556Srgrimes */ 6521556Srgrimes if (p == NULL) { 6531556Srgrimes /* Directories will be displayed later. */ 6541556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6551556Srgrimes cur->fts_number = NO_PRINT; 6561556Srgrimes continue; 6571556Srgrimes } 6581556Srgrimes } else { 6591556Srgrimes /* Only display dot file if -a/-A set. */ 660152256Smux if (cur->fts_name[0] == '.' && ((!f_listdot || 661152256Smux f_nolistdot) && !f_forcelistdot)) { 6621556Srgrimes cur->fts_number = NO_PRINT; 6631556Srgrimes continue; 6641556Srgrimes } 6651556Srgrimes } 6661556Srgrimes if (cur->fts_namelen > maxlen) 6671556Srgrimes maxlen = cur->fts_namelen; 66835417Sdes if (f_octal || f_octal_escape) { 66988602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 67088602Sjoe 67188602Sjoe if (t > maxlen) 67288602Sjoe maxlen = t; 67337932Shoek } 6741556Srgrimes if (needstats) { 6751556Srgrimes sp = cur->fts_statp; 6761556Srgrimes if (sp->st_blocks > maxblock) 6771556Srgrimes maxblock = sp->st_blocks; 6781556Srgrimes if (sp->st_ino > maxinode) 6791556Srgrimes maxinode = sp->st_ino; 6801556Srgrimes if (sp->st_nlink > maxnlink) 6811556Srgrimes maxnlink = sp->st_nlink; 6821556Srgrimes if (sp->st_size > maxsize) 6831556Srgrimes maxsize = sp->st_size; 6841556Srgrimes 6851556Srgrimes btotal += sp->st_blocks; 6861556Srgrimes if (f_longform) { 68749373Ssheldonh if (f_numericonly) { 68849373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 68949373Ssheldonh "%u", sp->st_uid); 69049373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 69149373Ssheldonh "%u", sp->st_gid); 69249373Ssheldonh user = nuser; 69349373Ssheldonh group = ngroup; 69449373Ssheldonh } else { 69549373Ssheldonh user = user_from_uid(sp->st_uid, 0); 69649373Ssheldonh group = group_from_gid(sp->st_gid, 0); 69749373Ssheldonh } 6981556Srgrimes if ((ulen = strlen(user)) > maxuser) 6991556Srgrimes maxuser = ulen; 7001556Srgrimes if ((glen = strlen(group)) > maxgroup) 7011556Srgrimes maxgroup = glen; 7021556Srgrimes if (f_flags) { 70361749Sjoe flags = fflagstostr(sp->st_flags); 70461749Sjoe if (flags != NULL && *flags == '\0') { 70561749Sjoe free(flags); 70661749Sjoe flags = strdup("-"); 70761749Sjoe } 70861749Sjoe if (flags == NULL) 70999744Sdillon err(1, "fflagstostr"); 71090150Smarkm flen = strlen(flags); 71190150Smarkm if (flen > (size_t)maxflags) 7121556Srgrimes maxflags = flen; 7131556Srgrimes } else 7141556Srgrimes flen = 0; 715105832Srwatson labelstr = NULL; 716105832Srwatson if (f_label) { 717105836Srwatson char name[PATH_MAX + 1]; 718105832Srwatson mac_t label; 719105832Srwatson int error; 720105832Srwatson 721105832Srwatson error = mac_prepare_file_label(&label); 722105832Srwatson if (error == -1) { 723108057Srwatson warn("MAC label for %s/%s", 724108057Srwatson cur->fts_parent->fts_path, 725108057Srwatson cur->fts_name); 726105832Srwatson goto label_out; 727105832Srwatson } 728105832Srwatson 729105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 730105836Srwatson snprintf(name, sizeof(name), 731105836Srwatson "%s", cur->fts_name); 732105836Srwatson else 733105836Srwatson snprintf(name, sizeof(name), 734108057Srwatson "%s/%s", cur->fts_parent-> 735108057Srwatson fts_accpath, cur->fts_name); 736105836Srwatson 737105832Srwatson if (options & FTS_LOGICAL) 738105836Srwatson error = mac_get_file(name, 739105836Srwatson label); 740105832Srwatson else 741105836Srwatson error = mac_get_link(name, 742105836Srwatson label); 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 751105832Srwatson error = mac_to_text(label, 752105832Srwatson &labelstr); 753105832Srwatson if (error == -1) { 754108057Srwatson warn("MAC label for %s/%s", 755108057Srwatson cur->fts_parent->fts_path, 756108057Srwatson cur->fts_name); 757105832Srwatson mac_free(label); 758105832Srwatson goto label_out; 759105832Srwatson } 760105832Srwatson mac_free(label); 761105832Srwatsonlabel_out: 762105832Srwatson if (labelstr == NULL) 763114047Srwatson labelstr = strdup("-"); 764105832Srwatson labelstrlen = strlen(labelstr); 765105832Srwatson if (labelstrlen > maxlabelstr) 766105832Srwatson maxlabelstr = labelstrlen; 76786922Sgreen } else 768105832Srwatson labelstrlen = 0; 7691556Srgrimes 770105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 77186922Sgreen ulen + glen + flen + 4)) == NULL) 77299744Sdillon err(1, "malloc"); 7731556Srgrimes 7741556Srgrimes np->user = &np->data[0]; 7751556Srgrimes (void)strcpy(np->user, user); 7761556Srgrimes np->group = &np->data[ulen + 1]; 7771556Srgrimes (void)strcpy(np->group, group); 7781556Srgrimes 7791556Srgrimes if (S_ISCHR(sp->st_mode) || 7801556Srgrimes S_ISBLK(sp->st_mode)) 7811556Srgrimes bcfile = 1; 7821556Srgrimes 7831556Srgrimes if (f_flags) { 7841556Srgrimes np->flags = &np->data[ulen + glen + 2]; 78588602Sjoe (void)strcpy(np->flags, flags); 78661749Sjoe free(flags); 7871556Srgrimes } 788105832Srwatson if (f_label) { 789105832Srwatson np->label = &np->data[ulen + glen + 2 79086922Sgreen + (f_flags ? flen + 1 : 0)]; 791105832Srwatson (void)strcpy(np->label, labelstr); 792105832Srwatson free(labelstr); 79386922Sgreen } 7941556Srgrimes cur->fts_pointer = np; 7951556Srgrimes } 7961556Srgrimes } 7971556Srgrimes ++entries; 7981556Srgrimes } 7991556Srgrimes 800130237Sdas /* 801130237Sdas * If there are no entries to display, we normally stop right 802130237Sdas * here. However, we must continue if we have to display the 803130237Sdas * total block count. In this case, we display the total only 804130237Sdas * on the second (p != NULL) pass. 805130237Sdas */ 806130237Sdas if (!entries && (!(f_longform || f_size) || p == NULL)) 8071556Srgrimes return; 8081556Srgrimes 8091556Srgrimes d.list = list; 8101556Srgrimes d.entries = entries; 8111556Srgrimes d.maxlen = maxlen; 8121556Srgrimes if (needstats) { 8131556Srgrimes d.bcfile = bcfile; 8141556Srgrimes d.btotal = btotal; 8151556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 8161556Srgrimes d.s_block = strlen(buf); 8171556Srgrimes d.s_flags = maxflags; 818105832Srwatson d.s_label = maxlabelstr; 8191556Srgrimes d.s_group = maxgroup; 8201556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 8211556Srgrimes d.s_inode = strlen(buf); 8221556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 8231556Srgrimes d.s_nlink = strlen(buf); 824114583Smarkm (void)snprintf(buf, sizeof(buf), "%ju", maxsize); 8251556Srgrimes d.s_size = strlen(buf); 8261556Srgrimes d.s_user = maxuser; 8271556Srgrimes } 8281556Srgrimes printfcn(&d); 8291556Srgrimes output = 1; 8301556Srgrimes 8311556Srgrimes if (f_longform) 8321556Srgrimes for (cur = list; cur; cur = cur->fts_link) 8331556Srgrimes free(cur->fts_pointer); 8341556Srgrimes} 8351556Srgrimes 8361556Srgrimes/* 8371556Srgrimes * Ordering for mastercmp: 8381556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 8391556Srgrimes * as larger than directories. Within either group, use the sort function. 8401556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8411556Srgrimes */ 8421556Srgrimesstatic int 843103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8441556Srgrimes{ 8451556Srgrimes int a_info, b_info; 8461556Srgrimes 8471556Srgrimes a_info = (*a)->fts_info; 8481556Srgrimes if (a_info == FTS_ERR) 8491556Srgrimes return (0); 8501556Srgrimes b_info = (*b)->fts_info; 8511556Srgrimes if (b_info == FTS_ERR) 8521556Srgrimes return (0); 8531556Srgrimes 8541556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8551556Srgrimes return (namecmp(*a, *b)); 8561556Srgrimes 85729560Ssef if (a_info != b_info && 85829560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8591556Srgrimes if (a_info == FTS_D) 8601556Srgrimes return (1); 86129560Ssef if (b_info == FTS_D) 8621556Srgrimes return (-1); 86329560Ssef } 86429560Ssef return (sortfcn(*a, *b)); 8651556Srgrimes} 866