ls.c revision 106479
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 106479 2002-11-06 01:18:12Z tjr $"); 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 */ 4511556Srgrimes ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 4521556Srgrimes 4531556Srgrimes while ((p = fts_read(ftsp)) != NULL) 4541556Srgrimes switch (p->fts_info) { 4551556Srgrimes case FTS_DC: 4561556Srgrimes warnx("%s: directory causes a cycle", p->fts_name); 4571556Srgrimes break; 4581556Srgrimes case FTS_DNR: 4591556Srgrimes case FTS_ERR: 4601556Srgrimes warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 46117852Sadam rval = 1; 4621556Srgrimes break; 4631556Srgrimes case FTS_D: 4641556Srgrimes if (p->fts_level != FTS_ROOTLEVEL && 4651556Srgrimes p->fts_name[0] == '.' && !f_listdot) 4661556Srgrimes break; 4671556Srgrimes 4681556Srgrimes /* 4691556Srgrimes * If already output something, put out a newline as 4701556Srgrimes * a separator. If multiple arguments, precede each 4711556Srgrimes * directory with its name. 4721556Srgrimes */ 473105390Stjr if (output) { 474105390Stjr putchar('\n'); 475105390Stjr printname(p->fts_path); 476105390Stjr puts(":"); 477105390Stjr } else if (argc > 1) { 478105390Stjr printname(p->fts_path); 479105390Stjr puts(":"); 4801556Srgrimes output = 1; 4811556Srgrimes } 4821556Srgrimes chp = fts_children(ftsp, ch_options); 483105832Srwatson display(p, chp, options); 4841556Srgrimes 4851556Srgrimes if (!f_recursive && chp != NULL) 4861556Srgrimes (void)fts_set(ftsp, p, FTS_SKIP); 4871556Srgrimes break; 48896681Sbillf default: 48996681Sbillf break; 4901556Srgrimes } 4911556Srgrimes if (errno) 4921556Srgrimes err(1, "fts_read"); 4931556Srgrimes} 4941556Srgrimes 4951556Srgrimes/* 4961556Srgrimes * Display() takes a linked list of FTSENT structures and passes the list 4971556Srgrimes * along with any other necessary information to the print function. P 4981556Srgrimes * points to the parent directory of the display list. 4991556Srgrimes */ 5001556Srgrimesstatic void 501105832Srwatsondisplay(FTSENT *p, FTSENT *list, int options) 5021556Srgrimes{ 5031556Srgrimes struct stat *sp; 5041556Srgrimes DISPLAY d; 5051556Srgrimes FTSENT *cur; 5061556Srgrimes NAMES *np; 50790150Smarkm off_t maxsize; 508105832Srwatson u_long btotal, labelstrlen, maxblock, maxinode, maxlen, maxnlink; 509105832Srwatson u_long maxlabelstr; 51090150Smarkm int bcfile, maxflags; 51190150Smarkm gid_t maxgroup; 51290150Smarkm uid_t maxuser; 51390150Smarkm size_t flen, ulen, glen; 51437932Shoek char *initmax; 5151556Srgrimes int entries, needstats; 51696649Sjmallett const char *user, *group; 517105832Srwatson char *flags, *labelstr = NULL; 51850050Ssheldonh char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 51950050Ssheldonh char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 52050050Ssheldonh char nuser[STRBUF_SIZEOF(gid_t) + 1]; 5211556Srgrimes 5221556Srgrimes /* 5231556Srgrimes * If list is NULL there are two possibilities: that the parent 5241556Srgrimes * directory p has no children, or that fts_children() returned an 5251556Srgrimes * error. We ignore the error case since it will be replicated 5261556Srgrimes * on the next call to fts_read() on the post-order visit to the 52746684Skris * directory p, and will be signaled in traverse(). 5281556Srgrimes */ 5291556Srgrimes if (list == NULL) 5301556Srgrimes return; 5311556Srgrimes 5321556Srgrimes needstats = f_inode || f_longform || f_size; 5331556Srgrimes flen = 0; 53437932Shoek btotal = 0; 53537932Shoek initmax = getenv("LS_COLWIDTHS"); 53637932Shoek /* Fields match -lios order. New ones should be added at the end. */ 537105832Srwatson maxlabelstr = maxblock = maxinode = maxlen = maxnlink = 53890150Smarkm maxuser = maxgroup = maxflags = maxsize = 0; 53937932Shoek if (initmax != NULL && *initmax != '\0') { 54037932Shoek char *initmax2, *jinitmax; 54137932Shoek int ninitmax; 54237932Shoek 54337932Shoek /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 54437932Shoek jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); 54537932Shoek if (jinitmax == NULL) 54699744Sdillon err(1, "malloc"); 54737932Shoek if (*initmax == ':') 54837932Shoek strcpy(initmax2, "0:"), initmax2 += 2; 54937932Shoek else 55037932Shoek *initmax2++ = *initmax, *initmax2 = '\0'; 55137932Shoek for (initmax++; *initmax != '\0'; initmax++) { 55237932Shoek if (initmax[-1] == ':' && initmax[0] == ':') { 55337932Shoek *initmax2++ = '0'; 55437932Shoek *initmax2++ = initmax[0]; 55537932Shoek initmax2[1] = '\0'; 55637932Shoek } else { 55737932Shoek *initmax2++ = initmax[0]; 55837932Shoek initmax2[1] = '\0'; 55937932Shoek } 56037932Shoek } 56188602Sjoe if (initmax2[-1] == ':') 56288602Sjoe strcpy(initmax2, "0"); 56337932Shoek 56437932Shoek ninitmax = sscanf(jinitmax, 56590150Smarkm " %lu : %lu : %lu : %i : %i : %i : %llu : %lu : %lu ", 56637932Shoek &maxinode, &maxblock, &maxnlink, &maxuser, 567105832Srwatson &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); 56837932Shoek f_notabs = 1; 56937932Shoek switch (ninitmax) { 57088602Sjoe case 0: 57188602Sjoe maxinode = 0; 572102410Scharnier /* FALLTHROUGH */ 57388602Sjoe case 1: 57488602Sjoe maxblock = 0; 575102410Scharnier /* FALLTHROUGH */ 57688602Sjoe case 2: 57788602Sjoe maxnlink = 0; 578102410Scharnier /* FALLTHROUGH */ 57988602Sjoe case 3: 58088602Sjoe maxuser = 0; 581102410Scharnier /* FALLTHROUGH */ 58288602Sjoe case 4: 58388602Sjoe maxgroup = 0; 584102410Scharnier /* FALLTHROUGH */ 58588602Sjoe case 5: 58688602Sjoe maxflags = 0; 587102410Scharnier /* FALLTHROUGH */ 58888602Sjoe case 6: 58988602Sjoe maxsize = 0; 590102410Scharnier /* FALLTHROUGH */ 59188602Sjoe case 7: 59288602Sjoe maxlen = 0; 593102410Scharnier /* FALLTHROUGH */ 59488602Sjoe case 8: 595105832Srwatson maxlabelstr = 0; 596102410Scharnier /* FALLTHROUGH */ 59761338Sache#ifdef COLORLS 59888602Sjoe if (!f_color) 59961337Sache#endif 60088602Sjoe f_notabs = 0; 601102410Scharnier /* FALLTHROUGH */ 60290150Smarkm default: 60396681Sbillf break; 60437932Shoek } 60537932Shoek maxinode = makenines(maxinode); 60637932Shoek maxblock = makenines(maxblock); 60737932Shoek maxnlink = makenines(maxnlink); 60837932Shoek maxsize = makenines(maxsize); 60990150Smarkm } 6101556Srgrimes bcfile = 0; 6117165Sjoerg flags = NULL; 6121556Srgrimes for (cur = list, entries = 0; cur; cur = cur->fts_link) { 6131556Srgrimes if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 6141556Srgrimes warnx("%s: %s", 6151556Srgrimes cur->fts_name, strerror(cur->fts_errno)); 6161556Srgrimes cur->fts_number = NO_PRINT; 61717852Sadam rval = 1; 6181556Srgrimes continue; 6191556Srgrimes } 6201556Srgrimes /* 6211556Srgrimes * P is NULL if list is the argv list, to which different rules 6221556Srgrimes * apply. 6231556Srgrimes */ 6241556Srgrimes if (p == NULL) { 6251556Srgrimes /* Directories will be displayed later. */ 6261556Srgrimes if (cur->fts_info == FTS_D && !f_listdir) { 6271556Srgrimes cur->fts_number = NO_PRINT; 6281556Srgrimes continue; 6291556Srgrimes } 6301556Srgrimes } else { 6311556Srgrimes /* Only display dot file if -a/-A set. */ 6321556Srgrimes if (cur->fts_name[0] == '.' && !f_listdot) { 6331556Srgrimes cur->fts_number = NO_PRINT; 6341556Srgrimes continue; 6351556Srgrimes } 6361556Srgrimes } 6371556Srgrimes if (cur->fts_namelen > maxlen) 6381556Srgrimes maxlen = cur->fts_namelen; 63935417Sdes if (f_octal || f_octal_escape) { 64088602Sjoe u_long t = len_octal(cur->fts_name, cur->fts_namelen); 64188602Sjoe 64288602Sjoe if (t > maxlen) 64388602Sjoe maxlen = t; 64437932Shoek } 6451556Srgrimes if (needstats) { 6461556Srgrimes sp = cur->fts_statp; 6471556Srgrimes if (sp->st_blocks > maxblock) 6481556Srgrimes maxblock = sp->st_blocks; 6491556Srgrimes if (sp->st_ino > maxinode) 6501556Srgrimes maxinode = sp->st_ino; 6511556Srgrimes if (sp->st_nlink > maxnlink) 6521556Srgrimes maxnlink = sp->st_nlink; 6531556Srgrimes if (sp->st_size > maxsize) 6541556Srgrimes maxsize = sp->st_size; 6551556Srgrimes 6561556Srgrimes btotal += sp->st_blocks; 6571556Srgrimes if (f_longform) { 65849373Ssheldonh if (f_numericonly) { 65949373Ssheldonh (void)snprintf(nuser, sizeof(nuser), 66049373Ssheldonh "%u", sp->st_uid); 66149373Ssheldonh (void)snprintf(ngroup, sizeof(ngroup), 66249373Ssheldonh "%u", sp->st_gid); 66349373Ssheldonh user = nuser; 66449373Ssheldonh group = ngroup; 66549373Ssheldonh } else { 66649373Ssheldonh user = user_from_uid(sp->st_uid, 0); 66749373Ssheldonh group = group_from_gid(sp->st_gid, 0); 66849373Ssheldonh } 6691556Srgrimes if ((ulen = strlen(user)) > maxuser) 6701556Srgrimes maxuser = ulen; 6711556Srgrimes if ((glen = strlen(group)) > maxgroup) 6721556Srgrimes maxgroup = glen; 6731556Srgrimes if (f_flags) { 67461749Sjoe flags = fflagstostr(sp->st_flags); 67561749Sjoe if (flags != NULL && *flags == '\0') { 67661749Sjoe free(flags); 67761749Sjoe flags = strdup("-"); 67861749Sjoe } 67961749Sjoe if (flags == NULL) 68099744Sdillon err(1, "fflagstostr"); 68190150Smarkm flen = strlen(flags); 68290150Smarkm if (flen > (size_t)maxflags) 6831556Srgrimes maxflags = flen; 6841556Srgrimes } else 6851556Srgrimes flen = 0; 686105832Srwatson labelstr = NULL; 687105832Srwatson if (f_label) { 688105836Srwatson char name[PATH_MAX + 1]; 689105832Srwatson mac_t label; 690105832Srwatson int error; 691105832Srwatson 692105832Srwatson error = mac_prepare_file_label(&label); 693105832Srwatson if (error == -1) { 694106479Stjr warn("%s", cur->fts_name); 695105832Srwatson goto label_out; 696105832Srwatson } 697105832Srwatson 698105836Srwatson if (cur->fts_level == FTS_ROOTLEVEL) 699105836Srwatson snprintf(name, sizeof(name), 700105836Srwatson "%s", cur->fts_name); 701105836Srwatson else 702105836Srwatson snprintf(name, sizeof(name), 703105836Srwatson "%s/%s", cur->fts_parent->fts_accpath, 704105836Srwatson cur->fts_name); 705105836Srwatson 706105832Srwatson if (options & FTS_LOGICAL) 707105836Srwatson error = mac_get_file(name, 708105836Srwatson label); 709105832Srwatson else 710105836Srwatson error = mac_get_link(name, 711105836Srwatson label); 712105832Srwatson if (error == -1) { 713106479Stjr warn("%s", cur->fts_name); 714105832Srwatson mac_free(label); 715105832Srwatson goto label_out; 716105832Srwatson } 717105832Srwatson 718105832Srwatson error = mac_to_text(label, 719105832Srwatson &labelstr); 720105832Srwatson if (error == -1) { 721106479Stjr warn("%s", cur->fts_name); 722105832Srwatson mac_free(label); 723105832Srwatson goto label_out; 724105832Srwatson } 725105832Srwatson mac_free(label); 726105832Srwatsonlabel_out: 727105832Srwatson if (labelstr == NULL) 728105832Srwatson labelstr = strdup(""); 729105832Srwatson labelstrlen = strlen(labelstr); 730105832Srwatson if (labelstrlen > maxlabelstr) 731105832Srwatson maxlabelstr = labelstrlen; 73286922Sgreen } else 733105832Srwatson labelstrlen = 0; 7341556Srgrimes 735105832Srwatson if ((np = malloc(sizeof(NAMES) + labelstrlen + 73686922Sgreen ulen + glen + flen + 4)) == NULL) 73799744Sdillon err(1, "malloc"); 7381556Srgrimes 7391556Srgrimes np->user = &np->data[0]; 7401556Srgrimes (void)strcpy(np->user, user); 7411556Srgrimes np->group = &np->data[ulen + 1]; 7421556Srgrimes (void)strcpy(np->group, group); 7431556Srgrimes 7441556Srgrimes if (S_ISCHR(sp->st_mode) || 7451556Srgrimes S_ISBLK(sp->st_mode)) 7461556Srgrimes bcfile = 1; 7471556Srgrimes 7481556Srgrimes if (f_flags) { 7491556Srgrimes np->flags = &np->data[ulen + glen + 2]; 75088602Sjoe (void)strcpy(np->flags, flags); 75161749Sjoe free(flags); 7521556Srgrimes } 753105832Srwatson if (f_label) { 754105832Srwatson np->label = &np->data[ulen + glen + 2 75586922Sgreen + (f_flags ? flen + 1 : 0)]; 756105832Srwatson (void)strcpy(np->label, labelstr); 757105832Srwatson free(labelstr); 75886922Sgreen } 7591556Srgrimes cur->fts_pointer = np; 7601556Srgrimes } 7611556Srgrimes } 7621556Srgrimes ++entries; 7631556Srgrimes } 7641556Srgrimes 7651556Srgrimes if (!entries) 7661556Srgrimes return; 7671556Srgrimes 7681556Srgrimes d.list = list; 7691556Srgrimes d.entries = entries; 7701556Srgrimes d.maxlen = maxlen; 7711556Srgrimes if (needstats) { 7721556Srgrimes d.bcfile = bcfile; 7731556Srgrimes d.btotal = btotal; 7741556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 7751556Srgrimes d.s_block = strlen(buf); 7761556Srgrimes d.s_flags = maxflags; 777105832Srwatson d.s_label = maxlabelstr; 7781556Srgrimes d.s_group = maxgroup; 7791556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 7801556Srgrimes d.s_inode = strlen(buf); 7811556Srgrimes (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 7821556Srgrimes d.s_nlink = strlen(buf); 7831556Srgrimes (void)snprintf(buf, sizeof(buf), "%qu", maxsize); 7841556Srgrimes d.s_size = strlen(buf); 7851556Srgrimes d.s_user = maxuser; 7861556Srgrimes } 7871556Srgrimes printfcn(&d); 7881556Srgrimes output = 1; 7891556Srgrimes 7901556Srgrimes if (f_longform) 7911556Srgrimes for (cur = list; cur; cur = cur->fts_link) 7921556Srgrimes free(cur->fts_pointer); 7931556Srgrimes} 7941556Srgrimes 7951556Srgrimes/* 7961556Srgrimes * Ordering for mastercmp: 7971556Srgrimes * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 7981556Srgrimes * as larger than directories. Within either group, use the sort function. 7991556Srgrimes * All other levels use the sort function. Error entries remain unsorted. 8001556Srgrimes */ 8011556Srgrimesstatic int 802103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 8031556Srgrimes{ 8041556Srgrimes int a_info, b_info; 8051556Srgrimes 8061556Srgrimes a_info = (*a)->fts_info; 8071556Srgrimes if (a_info == FTS_ERR) 8081556Srgrimes return (0); 8091556Srgrimes b_info = (*b)->fts_info; 8101556Srgrimes if (b_info == FTS_ERR) 8111556Srgrimes return (0); 8121556Srgrimes 8131556Srgrimes if (a_info == FTS_NS || b_info == FTS_NS) 8141556Srgrimes return (namecmp(*a, *b)); 8151556Srgrimes 81629560Ssef if (a_info != b_info && 81729560Ssef (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 8181556Srgrimes if (a_info == FTS_D) 8191556Srgrimes return (1); 82029560Ssef if (b_info == FTS_D) 8211556Srgrimes return (-1); 82229560Ssef } 82329560Ssef return (sortfcn(*a, *b)); 8241556Srgrimes} 82537932Shoek 82637932Shoek/* 82737932Shoek * Makenines() returns (10**n)-1. This is useful for converting a width 82837932Shoek * into a number that wide in decimal. 82937932Shoek */ 83037932Shoekstatic u_quad_t 83190110Simpmakenines(u_long n) 83237932Shoek{ 83337932Shoek u_long i; 83437932Shoek u_quad_t reg; 83537932Shoek 83637932Shoek reg = 1; 83737932Shoek /* Use a loop instead of pow(), since all values of n are small. */ 83837932Shoek for (i = 0; i < n; i++) 83937932Shoek reg *= 10; 84037932Shoek reg--; 84137932Shoek 84237932Shoek return reg; 84337932Shoek} 844