ls.c revision 114047
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1989, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Michael Fischbein. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3827958Sstevestatic const char copyright[] = 391556Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 401556Srgrimes The Regents of the University of California. All rights reserved.\n"; 4127967Ssteve#endif /* not lint */ 4227967Ssteve 4390153Smarkm#if 0 4427967Ssteve#ifndef lint 4527967Sstevestatic char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; 4690153Smarkm#endif /* not lint */ 4727967Ssteve#endif 4899109Sobrien#include <sys/cdefs.h> 4999109Sobrien__FBSDID("$FreeBSD: head/bin/ls/ls.c 114047 2003-04-26 02:31:26Z rwatson $"); 501556Srgrimes 511556Srgrimes#include <sys/types.h> 521556Srgrimes#include <sys/stat.h> 531556Srgrimes#include <sys/ioctl.h> 54105832Srwatson#include <sys/mac.h> 551556Srgrimes 561556Srgrimes#include <dirent.h> 571556Srgrimes#include <err.h> 581556Srgrimes#include <errno.h> 591556Srgrimes#include <fts.h> 6090878Simp#include <grp.h> 6150050Ssheldonh#include <limits.h> 6250050Ssheldonh#include <locale.h> 6390878Simp#include <pwd.h> 641556Srgrimes#include <stdio.h> 651556Srgrimes#include <stdlib.h> 661556Srgrimes#include <string.h> 6761289Sache#include <unistd.h> 6861268Sjoe#ifdef COLORLS 6961289Sache#include <termcap.h> 7061289Sache#include <signal.h> 7161268Sjoe#endif 721556Srgrimes 731556Srgrimes#include "ls.h" 741556Srgrimes#include "extern.h" 751556Srgrimes 7650050Ssheldonh/* 7750050Ssheldonh * Upward approximation of the maximum number of characters needed to 7850050Ssheldonh * represent a value of integral type t as a string, excluding the 7950050Ssheldonh * NUL terminator, with provision for a sign. 8050050Ssheldonh */ 8150051Ssheldonh#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) 8250050Ssheldonh 83105832Srwatsonstatic void display(FTSENT *, FTSENT *, int); 8490110Simpstatic u_quad_t makenines(u_long); 85103726Swollmanstatic int mastercmp(const FTSENT * const *, const FTSENT * const *); 8690110Simpstatic void traverse(int, char **, int); 871556Srgrimes 8890110Simpstatic void (*printfcn)(DISPLAY *); 8990110Simpstatic int (*sortfcn)(const FTSENT *, const FTSENT *); 901556Srgrimes 911556Srgrimeslong blocksize; /* block size units */ 921556Srgrimesint termwidth = 80; /* default terminal width */ 931556Srgrimes 941556Srgrimes/* flags */ 9590150Smarkm int f_accesstime; /* use time of last access */ 9690150Smarkm int f_flags; /* show flags associated with a file */ 9790150Smarkm int f_humanval; /* show human-readable file sizes */ 9890150Smarkm int f_inode; /* print inode */ 9990150Smarkmstatic int f_kblocks; /* print size in kilobytes */ 10090150Smarkmstatic int f_listdir; /* list actual directory, not contents */ 10190150Smarkmstatic int f_listdot; /* list files beginning with . */ 10290150Smarkm int f_longform; /* long listing format */ 10390150Smarkm int f_nonprint; /* show unprintables as ? */ 10490150Smarkmstatic int f_nosort; /* don't sort output */ 10590150Smarkm int f_notabs; /* don't use tab-separated multi-col output */ 10690150Smarkmstatic int f_numericonly; /* don't convert uid/gid to name */ 10790150Smarkm int f_octal; /* show unprintables as \xxx */ 10890150Smarkm int f_octal_escape; /* like f_octal but use C escapes if possible */ 10990150Smarkmstatic int f_recursive; /* ls subdirectories also */ 11090150Smarkmstatic int f_reversesort; /* reverse whatever sort is used */ 11190150Smarkm int f_sectime; /* print the real time for all files */ 11290150Smarkmstatic int f_singlecol; /* use single column output */ 11390150Smarkm int f_size; /* list size in short listing */ 11496892Stjr int f_slash; /* similar to f_type, but only for dirs */ 11596892Stjr int f_sortacross; /* sort across rows, not down columns */ 11690150Smarkm int f_statustime; /* use time of last mode change */ 117101677Sschweikh int f_stream; /* stream the output, separate with commas */ 11890150Smarkmstatic int f_timesort; /* sort by time vice name */ 11990150Smarkm int f_type; /* add type character for non-regular files */ 12090150Smarkmstatic int f_whiteout; /* show whiteout entries */ 121105832Srwatson int f_label; /* show MAC label */ 12261268Sjoe#ifdef COLORLS 12390150Smarkm int f_color; /* add type in color for non-regular files */ 12461271Sjoe 12561271Sjoechar *ansi_bgcol; /* ANSI sequence to set background colour */ 12661271Sjoechar *ansi_fgcol; /* ANSI sequence to set foreground colour */ 12761271Sjoechar *ansi_coloff; /* ANSI sequence to reset colours */ 12888583Sjoechar *attrs_off; /* ANSI sequence to turn off attributes */ 12988583Sjoechar *enter_bold; /* ANSI sequence to set color to bold mode */ 13061268Sjoe#endif 1311556Srgrimes 13290150Smarkmstatic int rval; 13317852Sadam 1341556Srgrimesint 13590110Simpmain(int argc, char *argv[]) 1361556Srgrimes{ 13788602Sjoe static char dot[] = ".", *dotav[] = {dot, NULL}; 1381556Srgrimes struct winsize win; 1391556Srgrimes int ch, fts_options, notused; 1401556Srgrimes char *p; 14161271Sjoe#ifdef COLORLS 14288602Sjoe char termcapbuf[1024]; /* termcap definition buffer */ 14388602Sjoe char tcapbuf[512]; /* capability buffer */ 14461271Sjoe char *bp = tcapbuf; 14561271Sjoe#endif 14661271Sjoe 14788602Sjoe (void)setlocale(LC_ALL, ""); 14811808Sache 1491556Srgrimes /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 1501556Srgrimes if (isatty(STDOUT_FILENO)) { 15197803Stjr termwidth = 80; 15297803Stjr if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 15397803Stjr termwidth = atoi(p); 15497803Stjr else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 15597803Stjr win.ws_col > 0) 1561556Srgrimes termwidth = win.ws_col; 15790150Smarkm f_nonprint = 1; 1585158Sjoerg } else { 1591556Srgrimes f_singlecol = 1; 1605158Sjoerg /* retrieve environment variable, in case of explicit -C */ 16190150Smarkm p = getenv("COLUMNS"); 16290150Smarkm if (p) 1635158Sjoerg termwidth = atoi(p); 1645158Sjoerg } 1651556Srgrimes 1661556Srgrimes /* Root is -A automatically. */ 1671556Srgrimes if (!getuid()) 1681556Srgrimes f_listdot = 1; 1691556Srgrimes 1701556Srgrimes fts_options = FTS_PHYSICAL; 17196892Stjr while ((ch = getopt(argc, argv, "1ABCFGHLPRTWZabcdfghiklmnopqrstuwx")) 17296892Stjr != -1) { 1731556Srgrimes switch (ch) { 1741556Srgrimes /* 17596892Stjr * The -1, -C, -x and -l options all override each other so 17696892Stjr * shell aliasing works right. 1771556Srgrimes */ 1781556Srgrimes case '1': 1791556Srgrimes f_singlecol = 1; 18090150Smarkm f_longform = 0; 18196892Stjr f_stream = 0; 1821556Srgrimes break; 18335417Sdes case 'B': 18435417Sdes f_nonprint = 0; 18535417Sdes f_octal = 1; 18688602Sjoe f_octal_escape = 0; 18735417Sdes break; 1881556Srgrimes case 'C': 18996892Stjr f_sortacross = f_longform = f_singlecol = 0; 1901556Srgrimes break; 1911556Srgrimes case 'l': 1921556Srgrimes f_longform = 1; 19390150Smarkm f_singlecol = 0; 19496892Stjr f_stream = 0; 1951556Srgrimes break; 19696892Stjr case 'x': 19796892Stjr f_sortacross = 1; 19896892Stjr f_longform = 0; 19996892Stjr f_singlecol = 0; 20096892Stjr break; 2011556Srgrimes /* The -c and -u options override each other. */ 2021556Srgrimes case 'c': 2031556Srgrimes f_statustime = 1; 2041556Srgrimes f_accesstime = 0; 2051556Srgrimes break; 2061556Srgrimes case 'u': 2071556Srgrimes f_accesstime = 1; 2081556Srgrimes f_statustime = 0; 2091556Srgrimes break; 2101556Srgrimes case 'F': 2111556Srgrimes f_type = 1; 21296892Stjr f_slash = 0; 2131556Srgrimes break; 21435426Sdes case 'H': 21588602Sjoe fts_options |= FTS_COMFOLLOW; 21635426Sdes break; 21761178Sjoe case 'G': 21864568Sjoe setenv("CLICOLOR", "", 1); 21961178Sjoe break; 2201556Srgrimes case 'L': 2211556Srgrimes fts_options &= ~FTS_PHYSICAL; 2221556Srgrimes fts_options |= FTS_LOGICAL; 2231556Srgrimes break; 22435426Sdes case 'P': 22588602Sjoe fts_options &= ~FTS_COMFOLLOW; 22635426Sdes fts_options &= ~FTS_LOGICAL; 22735426Sdes fts_options |= FTS_PHYSICAL; 22835426Sdes break; 2291556Srgrimes case 'R': 2301556Srgrimes f_recursive = 1; 2311556Srgrimes break; 2321556Srgrimes case 'a': 2331556Srgrimes fts_options |= FTS_SEEDOT; 2341556Srgrimes /* FALLTHROUGH */ 2351556Srgrimes case 'A': 2361556Srgrimes f_listdot = 1; 2371556Srgrimes break; 2381556Srgrimes /* The -d option turns off the -R option. */ 2391556Srgrimes case 'd': 2401556Srgrimes f_listdir = 1; 2411556Srgrimes f_recursive = 0; 2421556Srgrimes break; 2431556Srgrimes case 'f': 2441556Srgrimes f_nosort = 1; 2451556Srgrimes break; 24688602Sjoe case 'g': /* Compatibility with 4.3BSD. */ 2471556Srgrimes break; 24888591Sjoe case 'h': 24988591Sjoe f_humanval = 1; 25088591Sjoe break; 2511556Srgrimes case 'i': 2521556Srgrimes f_inode = 1; 2531556Srgrimes break; 2542889Spst case 'k': 2552889Spst f_kblocks = 1; 2562889Spst break; 25796892Stjr case 'm': 25896892Stjr f_stream = 1; 25996892Stjr f_singlecol = 0; 26096892Stjr f_longform = 0; 26196892Stjr break; 26249373Ssheldonh case 'n': 26349373Ssheldonh f_numericonly = 1; 26449373Ssheldonh break; 2651556Srgrimes case 'o': 2661556Srgrimes f_flags = 1; 2671556Srgrimes break; 26896892Stjr case 'p': 26996892Stjr f_slash = 1; 27096892Stjr f_type = 1; 27196892Stjr break; 2721556Srgrimes case 'q': 2731556Srgrimes f_nonprint = 1; 27435373Sdes f_octal = 0; 27588602Sjoe f_octal_escape = 0; 2761556Srgrimes break; 2771556Srgrimes case 'r': 2781556Srgrimes f_reversesort = 1; 2791556Srgrimes break; 2801556Srgrimes case 's': 2811556Srgrimes f_size = 1; 2821556Srgrimes break; 2831556Srgrimes case 'T': 2841556Srgrimes f_sectime = 1; 2851556Srgrimes break; 2861556Srgrimes case 't': 2871556Srgrimes f_timesort = 1; 2881556Srgrimes break; 28920417Ssteve case 'W': 29020417Ssteve f_whiteout = 1; 29120417Ssteve break; 29235373Sdes case 'b': 29335373Sdes f_nonprint = 0; 29488602Sjoe f_octal = 0; 29535417Sdes f_octal_escape = 1; 29635373Sdes break; 29788603Sjoe case 'w': 29888603Sjoe f_nonprint = 0; 29988603Sjoe f_octal = 0; 30088603Sjoe f_octal_escape = 0; 30188603Sjoe break; 30286922Sgreen case 'Z': 303105832Srwatson f_label = 1; 30486922Sgreen break; 3051556Srgrimes default: 3061556Srgrimes case '?': 3071556Srgrimes usage(); 3081556Srgrimes } 3091556Srgrimes } 3101556Srgrimes argc -= optind; 3111556Srgrimes argv += optind; 3121556Srgrimes 31364568Sjoe /* Enabling of colours is conditional on the environment. */ 31464568Sjoe if (getenv("CLICOLOR") && 31564568Sjoe (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 31664604Sjoe#ifdef COLORLS 31764568Sjoe if (tgetent(termcapbuf, getenv("TERM")) == 1) { 31864568Sjoe ansi_fgcol = tgetstr("AF", &bp); 31964568Sjoe ansi_bgcol = tgetstr("AB", &bp); 32088583Sjoe attrs_off = tgetstr("me", &bp); 32188583Sjoe enter_bold = tgetstr("md", &bp); 32264568Sjoe 32364568Sjoe /* To switch colours off use 'op' if 32464568Sjoe * available, otherwise use 'oc', or 32564568Sjoe * don't do colours at all. */ 32664568Sjoe ansi_coloff = tgetstr("op", &bp); 32764568Sjoe if (!ansi_coloff) 32864568Sjoe ansi_coloff = tgetstr("oc", &bp); 32964568Sjoe if (ansi_fgcol && ansi_bgcol && ansi_coloff) 33064568Sjoe f_color = 1; 33164568Sjoe } 33264604Sjoe#else 333106479Stjr warnx("color support not compiled in"); 33464604Sjoe#endif /*COLORLS*/ 33564568Sjoe 33664604Sjoe#ifdef COLORLS 33761289Sache if (f_color) { 33861337Sache /* 33961337Sache * We can't put tabs and color sequences together: 34061337Sache * column number will be incremented incorrectly 34161337Sache * for "stty oxtabs" mode. 34261337Sache */ 34361337Sache f_notabs = 1; 34488602Sjoe (void)signal(SIGINT, colorquit); 34588602Sjoe (void)signal(SIGQUIT, colorquit); 34661178Sjoe parsecolors(getenv("LSCOLORS")); 34761289Sache } 34861268Sjoe#endif 34961178Sjoe 3501556Srgrimes /* 3511556Srgrimes * If not -F, -i, -l, -s or -t options, don't require stat 35261178Sjoe * information, unless in color mode in which case we do 35361178Sjoe * need this to determine which colors to display. 3541556Srgrimes */ 35561178Sjoe if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type 35661268Sjoe#ifdef COLORLS 35761268Sjoe && !f_color 35861268Sjoe#endif 35988602Sjoe ) 3601556Srgrimes fts_options |= FTS_NOSTAT; 3611556Srgrimes 3621556Srgrimes /* 3631556Srgrimes * If not -F, -d or -l options, follow any symbolic links listed on 3641556Srgrimes * the command line. 3651556Srgrimes */ 3661556Srgrimes if (!f_longform && !f_listdir && !f_type) 3671556Srgrimes fts_options |= FTS_COMFOLLOW; 3681556Srgrimes 36920417Ssteve /* 37020417Ssteve * If -W, show whiteout entries 37120417Ssteve */ 37220417Ssteve#ifdef FTS_WHITEOUT 37320417Ssteve if (f_whiteout) 37420417Ssteve fts_options |= FTS_WHITEOUT; 37520417Ssteve#endif 37620417Ssteve 3771556Srgrimes /* If -l or -s, figure out block size. */ 3781556Srgrimes if (f_longform || f_size) { 3792889Spst if (f_kblocks) 3807282Sphk blocksize = 2; 3817282Sphk else { 3827282Sphk (void)getbsize(¬used, &blocksize); 3837282Sphk blocksize /= 512; 3847282Sphk } 3851556Srgrimes } 3861556Srgrimes /* Select a sort function. */ 3871556Srgrimes if (f_reversesort) { 3881556Srgrimes if (!f_timesort) 3891556Srgrimes sortfcn = revnamecmp; 3901556Srgrimes else if (f_accesstime) 3911556Srgrimes sortfcn = revacccmp; 3921556Srgrimes else if (f_statustime) 3931556Srgrimes sortfcn = revstatcmp; 39488602Sjoe else /* Use modification time. */ 3951556Srgrimes sortfcn = revmodcmp; 3961556Srgrimes } else { 3971556Srgrimes if (!f_timesort) 3981556Srgrimes sortfcn = namecmp; 3991556Srgrimes else if (f_accesstime) 4001556Srgrimes sortfcn = acccmp; 4011556Srgrimes else if (f_statustime) 4021556Srgrimes sortfcn = statcmp; 40388602Sjoe else /* Use modification time. */ 4041556Srgrimes sortfcn = modcmp; 4051556Srgrimes } 4061556Srgrimes 4071556Srgrimes /* Select a print function. */ 4081556Srgrimes if (f_singlecol) 4091556Srgrimes printfcn = printscol; 4101556Srgrimes else if (f_longform) 4111556Srgrimes printfcn = printlong; 41296892Stjr else if (f_stream) 41396892Stjr printfcn = printstream; 4141556Srgrimes else 4151556Srgrimes printfcn = printcol; 4161556Srgrimes 4171556Srgrimes if (argc) 4181556Srgrimes traverse(argc, argv, fts_options); 4191556Srgrimes else 4201556Srgrimes traverse(1, dotav, fts_options); 42117852Sadam exit(rval); 4221556Srgrimes} 4231556Srgrimes 42488602Sjoestatic int output; /* If anything output. */ 4251556Srgrimes 4261556Srgrimes/* 4271556Srgrimes * Traverse() walks the logical directory structure specified by the argv list 4281556Srgrimes * in the order specified by the mastercmp() comparison function. During the 4291556Srgrimes * traversal it passes linked lists of structures to display() which represent 4301556Srgrimes * a superset (may be exact set) of the files to be displayed. 4311556Srgrimes */ 4321556Srgrimesstatic void 43390110Simptraverse(int argc, char *argv[], int options) 4341556Srgrimes{ 4351556Srgrimes FTS *ftsp; 4361556Srgrimes FTSENT *p, *chp; 4371556Srgrimes int ch_options; 4381556Srgrimes 4391556Srgrimes if ((ftsp = 4401556Srgrimes fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 44199744Sdillon err(1, "fts_open"); 4421556Srgrimes 443105832Srwatson display(NULL, fts_children(ftsp, 0), options); 4441556Srgrimes if (f_listdir) 4451556Srgrimes return; 4461556Srgrimes 4471556Srgrimes /* 4481556Srgrimes * If not recursing down this tree and don't need stat info, just get 4491556Srgrimes * the names. 4501556Srgrimes */ 451108057Srwatson ch_options = !f_recursive && !f_label && 452108057Srwatson options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 4531556Srgrimes 4541556Srgrimes while ((p = fts_read(ftsp)) != NULL) 4551556Srgrimes switch (p->fts_info) { 4561556Srgrimes case FTS_DC: 4571556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 4581556Srgrimes break; 4591556Srgrimes case FTS_DNR: 4601556Srgrimes case FTS_ERR: 4611556Srgrimes warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 46217852Sadam rval = 1; 4631556Srgrimes break; 4641556Srgrimes case FTS_D: 4651556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 4661556Srgrimes p->fts_name[0] == '.' && !f_listdot) 4671556Srgrimes break; 4681556Srgrimes 4691556Srgrimes /* 4701556Srgrimes * If already output something, put out a newline as 4711556Srgrimes * a separator. If multiple arguments, precede each 4721556Srgrimes * directory with its name. 4731556Srgrimes */ 474105390Stjr if (output) { 475105390Stjr putchar('\n'); 476105390Stjr printname(p->fts_path); 477105390Stjr puts(":"); 478105390Stjr } else if (argc > 1) { 479105390Stjr printname(p->fts_path); 480105390Stjr puts(":"); 4811556Srgrimes output = 1; 4821556Srgrimes } 4831556Srgrimes chp = fts_children(ftsp, ch_options); 484105832Srwatson display(p, chp, options); 4851556Srgrimes 4861556Srgrimes if (!f_recursive && chp != NULL) 4871556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 4881556Srgrimes break; 48996681Sbillf default: 49096681Sbillf break; 4911556Srgrimes } 4921556Srgrimes if (errno) 4931556Srgrimes err(1, "fts_read"); 4941556Srgrimes} 4951556Srgrimes 4961556Srgrimes/* 4971556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 4981556Srgrimes * along with any other necessary information to the print function. P 4991556Srgrimes * points to the parent directory of the display list. 5001556Srgrimes */ 5011556Srgrimesstatic void 502105832Srwatsondisplay(FTSENT *p, FTSENT *list, int options) 5031556Srgrimes{ 5041556Srgrimes struct stat *sp; 5051556Srgrimes DISPLAY d; 5061556Srgrimes FTSENT *cur; 5071556Srgrimes NAMES *np; 50890150Smarkm off_t maxsize; 509105832Srwatson u_long btotal, labelstrlen, maxblock, maxinode, maxlen, maxnlink; 510105832Srwatson u_long maxlabelstr; 51190150Smarkm int bcfile, maxflags; 51290150Smarkm gid_t maxgroup; 51390150Smarkm uid_t maxuser; 51490150Smarkm size_t flen, ulen, glen; 51537932Shoek char *initmax; 5161556Srgrimes int entries, needstats; 51796649Sjmallett const char *user, *group; 518105832Srwatson char *flags, *labelstr = NULL; 51950050Ssheldonh char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 52050050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 52150050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5221556Srgrimes 5231556Srgrimes /* 5241556Srgrimes * If list is NULL there are two possibilities: that the parent 5251556Srgrimes * directory p has no children, or that fts_children() returned an 5261556Srgrimes * error. We ignore the error case since it will be replicated 5271556Srgrimes * on the next call to fts_read() on the post-order visit to the 52846684Skris * directory p, and will be signaled in traverse(). 5291556Srgrimes */ 5301556Srgrimes if (list == NULL) 5311556Srgrimes return; 5321556Srgrimes 5331556Srgrimes needstats = f_inode || f_longform || f_size; 5341556Srgrimes flen = 0; 53537932Shoek btotal = 0; 53637932Shoek initmax = getenv("LS_COLWIDTHS"); 53737932Shoek /* Fields match -lios order. New ones should be added at the end. */ 538105832Srwatson maxlabelstr = maxblock = maxinode = maxlen = maxnlink = 53990150Smarkm maxuser = maxgroup = maxflags = maxsize = 0; 54037932Shoek if (initmax != NULL && *initmax != '\0') { 54137932Shoek char *initmax2, *jinitmax; 54237932Shoek int ninitmax; 54337932Shoek 54437932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 54537932Shoek jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); 54637932Shoek if (jinitmax == NULL) 54799744Sdillon err(1, "malloc"); 54837932Shoek if (*initmax == ':') 54937932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 55037932Shoek else 55137932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 55237932Shoek for (initmax++; *initmax != '\0'; initmax++) { 55337932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 55437932Shoek *initmax2++ = '0'; 55537932Shoek *initmax2++ = initmax[0]; 55637932Shoek initmax2[1] = '\0'; 55737932Shoek } else { 55837932Shoek *initmax2++ = initmax[0]; 55937932Shoek initmax2[1] = '\0'; 56037932Shoek } 56137932Shoek } 56288602Sjoe if (initmax2[-1] == ':') 56388602Sjoe strcpy(initmax2, "0"); 56437932Shoek 56537932Shoek ninitmax = sscanf(jinitmax, 56690150Smarkm " %lu : %lu : %lu : %i : %i : %i : %llu : %lu : %lu ", 56737932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 568105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 56937932Shoek f_notabs = 1; 57037932Shoek switch (ninitmax) { 57188602Sjoe case 0: 57288602Sjoe maxinode = 0; 573102410Scharnier /* FALLTHROUGH */ 57488602Sjoe case 1: 57588602Sjoe maxblock = 0; 576102410Scharnier /* FALLTHROUGH */ 57788602Sjoe case 2: 57888602Sjoe maxnlink = 0; 579102410Scharnier /* FALLTHROUGH */ 58088602Sjoe case 3: 58188602Sjoe maxuser = 0; 582102410Scharnier /* FALLTHROUGH */ 58388602Sjoe case 4: 58488602Sjoe maxgroup = 0; 585102410Scharnier /* FALLTHROUGH */ 58688602Sjoe case 5: 58788602Sjoe maxflags = 0; 588102410Scharnier /* FALLTHROUGH */ 58988602Sjoe case 6: 59088602Sjoe maxsize = 0; 591102410Scharnier /* FALLTHROUGH */ 59288602Sjoe case 7: 59388602Sjoe maxlen = 0; 594102410Scharnier /* FALLTHROUGH */ 59588602Sjoe case 8: 596105832Srwatson maxlabelstr = 0; 597102410Scharnier /* FALLTHROUGH */ 59861338Sache#ifdef COLORLS 59988602Sjoe if (!f_color) 60061337Sache#endif 60188602Sjoe f_notabs = 0; 602102410Scharnier /* FALLTHROUGH */ 60390150Smarkm default: 60496681Sbillf break; 60537932Shoek } 60637932Shoek maxinode = makenines(maxinode); 60737932Shoek maxblock = makenines(maxblock); 60837932Shoek maxnlink = makenines(maxnlink); 60937932Shoek maxsize = makenines(maxsize); 61090150Smarkm } 6111556Srgrimes bcfile = 0; 6127165Sjoerg flags = NULL; 6131556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6141556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6151556Srgrimes warnx("%s: %s", 6161556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6171556Srgrimes cur->fts_number = NO_PRINT; 61817852Sadam rval = 1; 6191556Srgrimes continue; 6201556Srgrimes } 6211556Srgrimes /* 6221556Srgrimes * P is NULL if list is the argv list, to which different rules 6231556Srgrimes * apply. 6241556Srgrimes */ 6251556Srgrimes if (p == NULL) { 6261556Srgrimes /* Directories will be displayed later. */ 6271556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6281556Srgrimes cur->fts_number = NO_PRINT; 6291556Srgrimes continue; 6301556Srgrimes } 6311556Srgrimes } else { 6321556Srgrimes /* Only display dot file if -a/-A set. */ 6331556Srgrimes if (cur->fts_name[0] == '.' && !f_listdot) { 6341556Srgrimes cur->fts_number = NO_PRINT; 6351556Srgrimes continue; 6361556Srgrimes } 6371556Srgrimes } 6381556Srgrimes if (cur->fts_namelen > maxlen) 6391556Srgrimes maxlen = cur->fts_namelen; 64035417Sdes if (f_octal || f_octal_escape) { 64188602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 64288602Sjoe 64388602Sjoe if (t > maxlen) 64488602Sjoe maxlen = t; 64537932Shoek } 6461556Srgrimes if (needstats) { 6471556Srgrimes sp = cur->fts_statp; 6481556Srgrimes if (sp->st_blocks > maxblock) 6491556Srgrimes maxblock = sp->st_blocks; 6501556Srgrimes if (sp->st_ino > maxinode) 6511556Srgrimes maxinode = sp->st_ino; 6521556Srgrimes if (sp->st_nlink > maxnlink) 6531556Srgrimes maxnlink = sp->st_nlink; 6541556Srgrimes if (sp->st_size > maxsize) 6551556Srgrimes maxsize = sp->st_size; 6561556Srgrimes 6571556Srgrimes btotal += sp->st_blocks; 6581556Srgrimes if (f_longform) { 65949373Ssheldonh if (f_numericonly) { 66049373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 66149373Ssheldonh "%u", sp->st_uid); 66249373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 66349373Ssheldonh "%u", sp->st_gid); 66449373Ssheldonh user = nuser; 66549373Ssheldonh group = ngroup; 66649373Ssheldonh } else { 66749373Ssheldonh user = user_from_uid(sp->st_uid, 0); 66849373Ssheldonh group = group_from_gid(sp->st_gid, 0); 66949373Ssheldonh } 6701556Srgrimes if ((ulen = strlen(user)) > maxuser) 6711556Srgrimes maxuser = ulen; 6721556Srgrimes if ((glen = strlen(group)) > maxgroup) 6731556Srgrimes maxgroup = glen; 6741556Srgrimes if (f_flags) { 67561749Sjoe flags = fflagstostr(sp->st_flags); 67661749Sjoe if (flags != NULL && *flags == '\0') { 67761749Sjoe free(flags); 67861749Sjoe flags = strdup("-"); 67961749Sjoe } 68061749Sjoe if (flags == NULL) 68199744Sdillon err(1, "fflagstostr"); 68290150Smarkm flen = strlen(flags); 68390150Smarkm if (flen > (size_t)maxflags) 6841556Srgrimes maxflags = flen; 6851556Srgrimes } else 6861556Srgrimes flen = 0; 687105832Srwatson labelstr = NULL; 688105832Srwatson if (f_label) { 689105836Srwatson char name[PATH_MAX + 1]; 690105832Srwatson mac_t label; 691105832Srwatson int error; 692105832Srwatson 693105832Srwatson error = mac_prepare_file_label(&label); 694105832Srwatson if (error == -1) { 695108057Srwatson warn("MAC label for %s/%s", 696108057Srwatson cur->fts_parent->fts_path, 697108057Srwatson cur->fts_name); 698105832Srwatson goto label_out; 699105832Srwatson } 700105832Srwatson 701105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 702105836Srwatson snprintf(name, sizeof(name), 703105836Srwatson "%s", cur->fts_name); 704105836Srwatson else 705105836Srwatson snprintf(name, sizeof(name), 706108057Srwatson "%s/%s", cur->fts_parent-> 707108057Srwatson fts_accpath, cur->fts_name); 708105836Srwatson 709105832Srwatson if (options & FTS_LOGICAL) 710105836Srwatson error = mac_get_file(name, 711105836Srwatson label); 712105832Srwatson else 713105836Srwatson error = mac_get_link(name, 714105836Srwatson label); 715105832Srwatson if (error == -1) { 716108057Srwatson warn("MAC label for %s/%s", 717108057Srwatson cur->fts_parent->fts_path, 718108057Srwatson cur->fts_name); 719105832Srwatson mac_free(label); 720105832Srwatson goto label_out; 721105832Srwatson } 722105832Srwatson 723105832Srwatson error = mac_to_text(label, 724105832Srwatson &labelstr); 725105832Srwatson if (error == -1) { 726108057Srwatson warn("MAC label for %s/%s", 727108057Srwatson cur->fts_parent->fts_path, 728108057Srwatson cur->fts_name); 729105832Srwatson mac_free(label); 730105832Srwatson goto label_out; 731105832Srwatson } 732105832Srwatson mac_free(label); 733105832Srwatsonlabel_out: 734105832Srwatson if (labelstr == NULL) 735114047Srwatson labelstr = strdup("-"); 736105832Srwatson labelstrlen = strlen(labelstr); 737105832Srwatson if (labelstrlen > maxlabelstr) 738105832Srwatson maxlabelstr = labelstrlen; 73986922Sgreen } else 740105832Srwatson labelstrlen = 0; 7411556Srgrimes 742105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 74386922Sgreen ulen + glen + flen + 4)) == NULL) 74499744Sdillon err(1, "malloc"); 7451556Srgrimes 7461556Srgrimes np->user = &np->data[0]; 7471556Srgrimes (void)strcpy(np->user, user); 7481556Srgrimes np->group = &np->data[ulen + 1]; 7491556Srgrimes (void)strcpy(np->group, group); 7501556Srgrimes 7511556Srgrimes if (S_ISCHR(sp->st_mode) || 7521556Srgrimes S_ISBLK(sp->st_mode)) 7531556Srgrimes bcfile = 1; 7541556Srgrimes 7551556Srgrimes if (f_flags) { 7561556Srgrimes np->flags = &np->data[ulen + glen + 2]; 75788602Sjoe (void)strcpy(np->flags, flags); 75861749Sjoe free(flags); 7591556Srgrimes } 760105832Srwatson if (f_label) { 761105832Srwatson np->label = &np->data[ulen + glen + 2 76286922Sgreen + (f_flags ? flen + 1 : 0)]; 763105832Srwatson (void)strcpy(np->label, labelstr); 764105832Srwatson free(labelstr); 76586922Sgreen } 7661556Srgrimes cur->fts_pointer = np; 7671556Srgrimes } 7681556Srgrimes } 7691556Srgrimes ++entries; 7701556Srgrimes } 7711556Srgrimes 7721556Srgrimes if (!entries) 7731556Srgrimes return; 7741556Srgrimes 7751556Srgrimes d.list = list; 7761556Srgrimes d.entries = entries; 7771556Srgrimes d.maxlen = maxlen; 7781556Srgrimes if (needstats) { 7791556Srgrimes d.bcfile = bcfile; 7801556Srgrimes d.btotal = btotal; 7811556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 7821556Srgrimes d.s_block = strlen(buf); 7831556Srgrimes d.s_flags = maxflags; 784105832Srwatson d.s_label = maxlabelstr; 7851556Srgrimes d.s_group = maxgroup; 7861556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 7871556Srgrimes d.s_inode = strlen(buf); 7881556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 7891556Srgrimes d.s_nlink = strlen(buf); 7901556Srgrimes (void)snprintf(buf, sizeof(buf), "%qu", maxsize); 7911556Srgrimes d.s_size = strlen(buf); 7921556Srgrimes d.s_user = maxuser; 7931556Srgrimes } 7941556Srgrimes printfcn(&d); 7951556Srgrimes output = 1; 7961556Srgrimes 7971556Srgrimes if (f_longform) 7981556Srgrimes for (cur = list; cur; cur = cur->fts_link) 7991556Srgrimes free(cur->fts_pointer); 8001556Srgrimes} 8011556Srgrimes 8021556Srgrimes/* 8031556Srgrimes * Ordering for mastercmp: 8041556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 8051556Srgrimes * as larger than directories. Within either group, use the sort function. 8061556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8071556Srgrimes */ 8081556Srgrimesstatic int 809103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8101556Srgrimes{ 8111556Srgrimes int a_info, b_info; 8121556Srgrimes 8131556Srgrimes a_info = (*a)->fts_info; 8141556Srgrimes if (a_info == FTS_ERR) 8151556Srgrimes return (0); 8161556Srgrimes b_info = (*b)->fts_info; 8171556Srgrimes if (b_info == FTS_ERR) 8181556Srgrimes return (0); 8191556Srgrimes 8201556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8211556Srgrimes return (namecmp(*a, *b)); 8221556Srgrimes 82329560Ssef if (a_info != b_info && 82429560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8251556Srgrimes if (a_info == FTS_D) 8261556Srgrimes return (1); 82729560Ssef if (b_info == FTS_D) 8281556Srgrimes return (-1); 82929560Ssef } 83029560Ssef return (sortfcn(*a, *b)); 8311556Srgrimes} 83237932Shoek 83337932Shoek/* 83437932Shoek * Makenines() returns (10**n)-1. This is useful for converting a width 83537932Shoek * into a number that wide in decimal. 83637932Shoek */ 83737932Shoekstatic u_quad_t 83890110Simpmakenines(u_long n) 83937932Shoek{ 84037932Shoek u_long i; 84137932Shoek u_quad_t reg; 84237932Shoek 84337932Shoek reg = 1; 84437932Shoek /* Use a loop instead of pow(), since all values of n are small. */ 84537932Shoek for (i = 0; i < n; i++) 84637932Shoek reg *= 10; 84737932Shoek reg--; 84837932Shoek 84937932Shoek return reg; 85037932Shoek} 851