ls.c revision 99109
155682Smarkm/* 2233294Sstas * Copyright (c) 1989, 1993, 1994 3233294Sstas * The Regents of the University of California. All rights reserved. 4233294Sstas * 555682Smarkm * This code is derived from software contributed to Berkeley by 6233294Sstas * Michael Fischbein. 7233294Sstas * 8233294Sstas * Redistribution and use in source and binary forms, with or without 955682Smarkm * modification, are permitted provided that the following conditions 10233294Sstas * are met: 11233294Sstas * 1. Redistributions of source code must retain the above copyright 1255682Smarkm * notice, this list of conditions and the following disclaimer. 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 3. All advertising materials mentioning features or use of this software 1755682Smarkm * must display the following acknowledgement: 1855682Smarkm * This product includes software developed by the University of 1955682Smarkm * California, Berkeley and its contributors. 2055682Smarkm * 4. Neither the name of the University nor the names of its contributors 2155682Smarkm * may be used to endorse or promote products derived from this software 2255682Smarkm * without specific prior written permission. 2355682Smarkm * 2455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2555682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2655682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2755682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2855682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2955682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3055682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3155682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3255682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3372445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3455682Smarkm * SUCH DAMAGE. 3555682Smarkm */ 36233294Sstas 3755682Smarkm#ifndef lint 3872445Sassarstatic const char copyright[] = 3972445Sassar"@(#) Copyright (c) 1989, 1993, 1994\n\ 4072445Sassar The Regents of the University of California. All rights reserved.\n"; 4172445Sassar#endif /* not lint */ 4272445Sassar 4372445Sassar#if 0 4472445Sassar#ifndef lint 4572445Sassarstatic char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; 4672445Sassar#endif /* not lint */ 4772445Sassar#endif 4872445Sassar#include <sys/cdefs.h> 4972445Sassar__FBSDID("$FreeBSD: head/bin/ls/ls.c 99109 2002-06-30 05:13:54Z obrien $"); 5072445Sassar 5172445Sassar#include <sys/types.h> 5290926Snectar#include <sys/stat.h> 5390926Snectar#include <sys/ioctl.h> 5490926Snectar 5572445Sassar#include <dirent.h> 5672445Sassar#include <err.h> 5772445Sassar#include <errno.h> 5890926Snectar#include <fts.h> 5990926Snectar#include <grp.h> 6090926Snectar#include <limits.h> 6190926Snectar#include <locale.h> 6290926Snectar#include <pwd.h> 6390926Snectar#include <stdio.h> 6490926Snectar#include <stdlib.h> 6590926Snectar#include <string.h> 6690926Snectar#include <unistd.h> 6772445Sassar#ifdef COLORLS 6872445Sassar#include <termcap.h> 6972445Sassar#include <signal.h> 7072445Sassar#endif 7155682Smarkm 7255682Smarkm#include "ls.h" 7355682Smarkm#include "extern.h" 7455682Smarkm#include "lomac.h" 7555682Smarkm 7655682Smarkm/* 7755682Smarkm * Upward approximation of the maximum number of characters needed to 7855682Smarkm * represent a value of integral type t as a string, excluding the 7955682Smarkm * NUL terminator, with provision for a sign. 8055682Smarkm */ 8155682Smarkm#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) 8255682Smarkm 8355682Smarkmstatic void display(FTSENT *, FTSENT *); 8455682Smarkmstatic u_quad_t makenines(u_long); 8555682Smarkmstatic int mastercmp(const FTSENT **, const FTSENT **); 8655682Smarkmstatic void traverse(int, char **, int); 8755682Smarkm 8855682Smarkmstatic void (*printfcn)(DISPLAY *); 8955682Smarkmstatic int (*sortfcn)(const FTSENT *, const FTSENT *); 9055682Smarkm 9155682Smarkmlong blocksize; /* block size units */ 9255682Smarkmint termwidth = 80; /* default terminal width */ 9355682Smarkm 9455682Smarkm/* flags */ 9555682Smarkm int f_accesstime; /* use time of last access */ 9655682Smarkm int f_flags; /* show flags associated with a file */ 9755682Smarkm int f_humanval; /* show human-readable file sizes */ 9855682Smarkm int f_inode; /* print inode */ 9955682Smarkmstatic int f_kblocks; /* print size in kilobytes */ 10072445Sassarstatic int f_listdir; /* list actual directory, not contents */ 10172445Sassarstatic int f_listdot; /* list files beginning with . */ 10272445Sassar int f_longform; /* long listing format */ 10355682Smarkm int f_nonprint; /* show unprintables as ? */ 10472445Sassarstatic int f_nosort; /* don't sort output */ 10572445Sassar int f_notabs; /* don't use tab-separated multi-col output */ 10672445Sassarstatic int f_numericonly; /* don't convert uid/gid to name */ 10772445Sassar int f_octal; /* show unprintables as \xxx */ 10855682Smarkm int f_octal_escape; /* like f_octal but use C escapes if possible */ 10972445Sassarstatic int f_recursive; /* ls subdirectories also */ 11072445Sassarstatic int f_reversesort; /* reverse whatever sort is used */ 11172445Sassar int f_sectime; /* print the real time for all files */ 11272445Sassarstatic int f_singlecol; /* use single column output */ 11372445Sassar int f_size; /* list size in short listing */ 11472445Sassar int f_slash; /* similar to f_type, but only for dirs */ 11572445Sassar int f_sortacross; /* sort across rows, not down columns */ 11672445Sassar int f_statustime; /* use time of last mode change */ 11790926Snectar int f_stream; /* stream the output, seperate with commas */ 11890926Snectarstatic int f_timesort; /* sort by time vice name */ 11990926Snectar int f_type; /* add type character for non-regular files */ 12090926Snectarstatic int f_whiteout; /* show whiteout entries */ 12190926Snectar int f_lomac; /* show LOMAC attributes */ 12255682Smarkm#ifdef COLORLS 12355682Smarkm int f_color; /* add type in color for non-regular files */ 12455682Smarkm 12555682Smarkmchar *ansi_bgcol; /* ANSI sequence to set background colour */ 12655682Smarkmchar *ansi_fgcol; /* ANSI sequence to set foreground colour */ 127102644Snectarchar *ansi_coloff; /* ANSI sequence to reset colours */ 128102644Snectarchar *attrs_off; /* ANSI sequence to turn off attributes */ 129102644Snectarchar *enter_bold; /* ANSI sequence to set color to bold mode */ 130102644Snectar#endif 13155682Smarkm 13255682Smarkmstatic int rval; 13355682Smarkm 13455682Smarkmint 13555682Smarkmmain(int argc, char *argv[]) 13655682Smarkm{ 13755682Smarkm static char dot[] = ".", *dotav[] = {dot, NULL}; 13855682Smarkm struct winsize win; 13990926Snectar int ch, fts_options, notused; 14090926Snectar char *p; 14190926Snectar#ifdef COLORLS 14290926Snectar char termcapbuf[1024]; /* termcap definition buffer */ 14390926Snectar char tcapbuf[512]; /* capability buffer */ 14490926Snectar char *bp = tcapbuf; 14590926Snectar#endif 14690926Snectar 14790926Snectar (void)setlocale(LC_ALL, ""); 14890926Snectar 149178825Sdfr /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 15090926Snectar if (isatty(STDOUT_FILENO)) { 15155682Smarkm termwidth = 80; 15255682Smarkm if ((p = getenv("COLUMNS")) != NULL && *p != '\0') 15372445Sassar termwidth = atoi(p); 15455682Smarkm else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 155233294Sstas win.ws_col > 0) 15655682Smarkm termwidth = win.ws_col; 15790926Snectar f_nonprint = 1; 158233294Sstas } else { 15972445Sassar f_singlecol = 1; 16055682Smarkm /* retrieve environment variable, in case of explicit -C */ 16172445Sassar p = getenv("COLUMNS"); 16272445Sassar if (p) 16355682Smarkm termwidth = atoi(p); 16455682Smarkm } 16555682Smarkm 16655682Smarkm /* Root is -A automatically. */ 16772445Sassar if (!getuid()) 16855682Smarkm f_listdot = 1; 16972445Sassar 17072445Sassar fts_options = FTS_PHYSICAL; 17172445Sassar while ((ch = getopt(argc, argv, "1ABCFGHLPRTWZabcdfghiklmnopqrstuwx")) 17272445Sassar != -1) { 17355682Smarkm switch (ch) { 17472445Sassar /* 17572445Sassar * The -1, -C, -x and -l options all override each other so 17672445Sassar * shell aliasing works right. 17755682Smarkm */ 17872445Sassar case '1': 17972445Sassar f_singlecol = 1; 18072445Sassar f_longform = 0; 18155682Smarkm f_stream = 0; 18272445Sassar break; 18372445Sassar case 'B': 18455682Smarkm f_nonprint = 0; 18572445Sassar f_octal = 1; 18655682Smarkm f_octal_escape = 0; 18772445Sassar break; 18872445Sassar case 'C': 18955682Smarkm f_sortacross = f_longform = f_singlecol = 0; 190233294Sstas break; 19155682Smarkm case 'l': 19255682Smarkm f_longform = 1; 193233294Sstas f_singlecol = 0; 19455682Smarkm f_stream = 0; 19555682Smarkm break; 19655682Smarkm case 'x': 19755682Smarkm f_sortacross = 1; 19855682Smarkm f_longform = 0; 19955682Smarkm f_singlecol = 0; 20055682Smarkm break; 20155682Smarkm /* The -c and -u options override each other. */ 20255682Smarkm case 'c': 20355682Smarkm f_statustime = 1; 20455682Smarkm f_accesstime = 0; 20555682Smarkm break; 20655682Smarkm case 'u': 20755682Smarkm f_accesstime = 1; 20855682Smarkm f_statustime = 0; 20955682Smarkm break; 21055682Smarkm case 'F': 21155682Smarkm f_type = 1; 21255682Smarkm f_slash = 0; 21355682Smarkm break; 21455682Smarkm case 'H': 21555682Smarkm fts_options |= FTS_COMFOLLOW; 21655682Smarkm break; 21755682Smarkm case 'G': 21855682Smarkm setenv("CLICOLOR", "", 1); 21955682Smarkm break; 22055682Smarkm case 'L': 221178825Sdfr fts_options &= ~FTS_PHYSICAL; 222178825Sdfr fts_options |= FTS_LOGICAL; 223178825Sdfr break; 224178825Sdfr case 'P': 22555682Smarkm fts_options &= ~FTS_COMFOLLOW; 226178825Sdfr fts_options &= ~FTS_LOGICAL; 227178825Sdfr fts_options |= FTS_PHYSICAL; 228178825Sdfr break; 229178825Sdfr case 'R': 23055682Smarkm f_recursive = 1; 23155682Smarkm break; 23255682Smarkm case 'a': 23355682Smarkm fts_options |= FTS_SEEDOT; 234178825Sdfr /* FALLTHROUGH */ 235178825Sdfr case 'A': 236178825Sdfr f_listdot = 1; 237178825Sdfr break; 23855682Smarkm /* The -d option turns off the -R option. */ 239178825Sdfr case 'd': 240178825Sdfr f_listdir = 1; 241178825Sdfr f_recursive = 0; 242178825Sdfr break; 24355682Smarkm case 'f': 244233294Sstas f_nosort = 1; 24555682Smarkm break; 24655682Smarkm case 'g': /* Compatibility with 4.3BSD. */ 247178825Sdfr break; 248178825Sdfr case 'h': 249178825Sdfr f_humanval = 1; 250178825Sdfr break; 25155682Smarkm case 'i': 25255682Smarkm f_inode = 1; 253178825Sdfr break; 254178825Sdfr case 'k': 255178825Sdfr f_kblocks = 1; 256178825Sdfr break; 25755682Smarkm case 'm': 258178825Sdfr f_stream = 1; 259178825Sdfr f_singlecol = 0; 260178825Sdfr f_longform = 0; 261178825Sdfr break; 262178825Sdfr case 'n': 263178825Sdfr f_numericonly = 1; 264178825Sdfr break; 265178825Sdfr case 'o': 26655682Smarkm f_flags = 1; 26755682Smarkm break; 26855682Smarkm case 'p': 26972445Sassar f_slash = 1; 27072445Sassar f_type = 1; 27172445Sassar break; 27272445Sassar case 'q': 27355682Smarkm f_nonprint = 1; 27455682Smarkm f_octal = 0; 27555682Smarkm f_octal_escape = 0; 27655682Smarkm break; 277178825Sdfr case 'r': 278178825Sdfr f_reversesort = 1; 279178825Sdfr break; 280178825Sdfr case 's': 28155682Smarkm f_size = 1; 28255682Smarkm break; 28355682Smarkm case 'T': 28455682Smarkm f_sectime = 1; 28555682Smarkm break; 28655682Smarkm case 't': 28755682Smarkm f_timesort = 1; 288178825Sdfr break; 289178825Sdfr case 'W': 290178825Sdfr f_whiteout = 1; 291178825Sdfr break; 29272445Sassar case 'b': 293178825Sdfr f_nonprint = 0; 294178825Sdfr f_octal = 0; 295178825Sdfr f_octal_escape = 1; 296178825Sdfr break; 29755682Smarkm case 'w': 29855682Smarkm f_nonprint = 0; 29955682Smarkm f_octal = 0; 300120945Snectar f_octal_escape = 0; 30155682Smarkm break; 30255682Smarkm case 'Z': 30355682Smarkm f_lomac = 1; 304178825Sdfr break; 305178825Sdfr default: 306178825Sdfr case '?': 307178825Sdfr usage(); 30855682Smarkm } 30990926Snectar } 31055682Smarkm argc -= optind; 311178825Sdfr argv += optind; 31255682Smarkm 31355682Smarkm /* Enabling of colours is conditional on the environment. */ 31455682Smarkm if (getenv("CLICOLOR") && 31555682Smarkm (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) 31655682Smarkm#ifdef COLORLS 31755682Smarkm if (tgetent(termcapbuf, getenv("TERM")) == 1) { 31855682Smarkm ansi_fgcol = tgetstr("AF", &bp); 31955682Smarkm ansi_bgcol = tgetstr("AB", &bp); 32055682Smarkm attrs_off = tgetstr("me", &bp); 32155682Smarkm enter_bold = tgetstr("md", &bp); 32255682Smarkm 32355682Smarkm /* To switch colours off use 'op' if 32455682Smarkm * available, otherwise use 'oc', or 32555682Smarkm * don't do colours at all. */ 32655682Smarkm ansi_coloff = tgetstr("op", &bp); 32755682Smarkm if (!ansi_coloff) 32855682Smarkm ansi_coloff = tgetstr("oc", &bp); 32955682Smarkm if (ansi_fgcol && ansi_bgcol && ansi_coloff) 33055682Smarkm f_color = 1; 33155682Smarkm } 33255682Smarkm#else 33355682Smarkm (void)fprintf(stderr, "Color support not compiled in.\n"); 33455682Smarkm#endif /*COLORLS*/ 33555682Smarkm 33655682Smarkm#ifdef COLORLS 33755682Smarkm if (f_color) { 33855682Smarkm /* 33955682Smarkm * We can't put tabs and color sequences together: 34055682Smarkm * column number will be incremented incorrectly 34155682Smarkm * for "stty oxtabs" mode. 34255682Smarkm */ 34355682Smarkm f_notabs = 1; 34455682Smarkm (void)signal(SIGINT, colorquit); 34555682Smarkm (void)signal(SIGQUIT, colorquit); 34655682Smarkm parsecolors(getenv("LSCOLORS")); 34755682Smarkm } 34855682Smarkm#endif 34955682Smarkm 35055682Smarkm /* 35155682Smarkm * If not -F, -i, -l, -s or -t options, don't require stat 35255682Smarkm * information, unless in color mode in which case we do 35355682Smarkm * need this to determine which colors to display. 35455682Smarkm */ 35555682Smarkm if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type 35655682Smarkm#ifdef COLORLS 35755682Smarkm && !f_color 35855682Smarkm#endif 35955682Smarkm ) 36055682Smarkm fts_options |= FTS_NOSTAT; 36155682Smarkm 36255682Smarkm /* 36355682Smarkm * If not -F, -d or -l options, follow any symbolic links listed on 36455682Smarkm * the command line. 36555682Smarkm */ 36655682Smarkm if (!f_longform && !f_listdir && !f_type) 36755682Smarkm fts_options |= FTS_COMFOLLOW; 36855682Smarkm 36955682Smarkm /* 37055682Smarkm * If -W, show whiteout entries 37155682Smarkm */ 37255682Smarkm#ifdef FTS_WHITEOUT 37355682Smarkm if (f_whiteout) 37455682Smarkm fts_options |= FTS_WHITEOUT; 37555682Smarkm#endif 37655682Smarkm 37772445Sassar /* If -l or -s, figure out block size. */ 37855682Smarkm if (f_longform || f_size) { 37955682Smarkm if (f_kblocks) 38055682Smarkm blocksize = 2; 38155682Smarkm else { 38255682Smarkm (void)getbsize(¬used, &blocksize); 38355682Smarkm blocksize /= 512; 38455682Smarkm } 38555682Smarkm } 38655682Smarkm /* Select a sort function. */ 38772445Sassar if (f_reversesort) { 38855682Smarkm if (!f_timesort) 38955682Smarkm sortfcn = revnamecmp; 390102644Snectar else if (f_accesstime) 39155682Smarkm sortfcn = revacccmp; 39255682Smarkm else if (f_statustime) 393178825Sdfr sortfcn = revstatcmp; 39455682Smarkm else /* Use modification time. */ 39555682Smarkm sortfcn = revmodcmp; 39655682Smarkm } else { 39755682Smarkm if (!f_timesort) 39855682Smarkm sortfcn = namecmp; 39955682Smarkm else if (f_accesstime) 40055682Smarkm sortfcn = acccmp; 40155682Smarkm else if (f_statustime) 40255682Smarkm sortfcn = statcmp; 40355682Smarkm else /* Use modification time. */ 40455682Smarkm sortfcn = modcmp; 40555682Smarkm } 40655682Smarkm 40755682Smarkm /* Select a print function. */ 408233294Sstas if (f_singlecol) 40972445Sassar printfcn = printscol; 41072445Sassar else if (f_longform) 41172445Sassar printfcn = printlong; 41255682Smarkm else if (f_stream) 41355682Smarkm printfcn = printstream; 41455682Smarkm else 415233294Sstas printfcn = printcol; 41672445Sassar 417233294Sstas if (argc) 41855682Smarkm traverse(argc, argv, fts_options); 41972445Sassar else 42072445Sassar traverse(1, dotav, fts_options); 42155682Smarkm exit(rval); 42255682Smarkm} 42372445Sassar 42455682Smarkmstatic int output; /* If anything output. */ 42555682Smarkm 42655682Smarkm/* 42755682Smarkm * Traverse() walks the logical directory structure specified by the argv list 428233294Sstas * in the order specified by the mastercmp() comparison function. During the 42955682Smarkm * traversal it passes linked lists of structures to display() which represent 43055682Smarkm * a superset (may be exact set) of the files to be displayed. 43155682Smarkm */ 432233294Sstasstatic void 43355682Smarkmtraverse(int argc, char *argv[], int options) 434233294Sstas{ 43555682Smarkm FTS *ftsp; 43655682Smarkm FTSENT *p, *chp; 43755682Smarkm int ch_options; 438233294Sstas 43955682Smarkm if ((ftsp = 440233294Sstas fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 44155682Smarkm err(1, NULL); 44255682Smarkm 44372445Sassar display(NULL, fts_children(ftsp, 0)); 44472445Sassar if (f_listdir) 44572445Sassar return; 44672445Sassar 44772445Sassar /* 44872445Sassar * If not recursing down this tree and don't need stat info, just get 44972445Sassar * the names. 45072445Sassar */ 45172445Sassar ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 45272445Sassar 45372445Sassar while ((p = fts_read(ftsp)) != NULL) 45472445Sassar switch (p->fts_info) { 45572445Sassar case FTS_DC: 45672445Sassar warnx("%s: directory causes a cycle", p->fts_name); 45772445Sassar break; 45872445Sassar case FTS_DNR: 45972445Sassar case FTS_ERR: 460233294Sstas warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 46155682Smarkm rval = 1; 46255682Smarkm break; 463233294Sstas case FTS_D: 46472445Sassar if (p->fts_level != FTS_ROOTLEVEL && 46555682Smarkm p->fts_name[0] == '.' && !f_listdot) 46655682Smarkm break; 46755682Smarkm 46855682Smarkm /* 46955682Smarkm * If already output something, put out a newline as 47055682Smarkm * a separator. If multiple arguments, precede each 47155682Smarkm * directory with its name. 47255682Smarkm */ 47355682Smarkm if (output) 47455682Smarkm (void)printf("\n%s:\n", p->fts_path); 47555682Smarkm else if (argc > 1) { 476233294Sstas (void)printf("%s:\n", p->fts_path); 47755682Smarkm output = 1; 47855682Smarkm } 47955682Smarkm chp = fts_children(ftsp, ch_options); 48055682Smarkm display(p, chp); 48172445Sassar 48255682Smarkm if (!f_recursive && chp != NULL) 48355682Smarkm (void)fts_set(ftsp, p, FTS_SKIP); 48455682Smarkm break; 48555682Smarkm default: 48655682Smarkm break; 48755682Smarkm } 48855682Smarkm if (errno) 48955682Smarkm err(1, "fts_read"); 49055682Smarkm} 49155682Smarkm 49255682Smarkm/* 49355682Smarkm * Display() takes a linked list of FTSENT structures and passes the list 49455682Smarkm * along with any other necessary information to the print function. P 49555682Smarkm * points to the parent directory of the display list. 49655682Smarkm */ 497233294Sstasstatic void 49855682Smarkmdisplay(FTSENT *p, FTSENT *list) 49955682Smarkm{ 50055682Smarkm struct stat *sp; 50190926Snectar DISPLAY d; 50290926Snectar FTSENT *cur; 50390926Snectar NAMES *np; 504102644Snectar off_t maxsize; 50555682Smarkm u_long btotal, lattrlen, maxblock, maxinode, maxlen, maxnlink, maxlattr; 50655682Smarkm int bcfile, maxflags; 50755682Smarkm gid_t maxgroup; 50855682Smarkm uid_t maxuser; 50990926Snectar size_t flen, ulen, glen; 51090926Snectar char *initmax; 51190926Snectar int entries, needstats; 512102644Snectar const char *user, *group; 51355682Smarkm char *flags, *lattr = NULL; 514102644Snectar char buf[STRBUF_SIZEOF(u_quad_t) + 1]; 515102644Snectar char ngroup[STRBUF_SIZEOF(uid_t) + 1]; 516102644Snectar char nuser[STRBUF_SIZEOF(gid_t) + 1]; 51790926Snectar 51890926Snectar /* 51990926Snectar * If list is NULL there are two possibilities: that the parent 52055682Smarkm * directory p has no children, or that fts_children() returned an 52155682Smarkm * error. We ignore the error case since it will be replicated 522102644Snectar * on the next call to fts_read() on the post-order visit to the 523102644Snectar * directory p, and will be signaled in traverse(). 52455682Smarkm */ 52555682Smarkm if (list == NULL) 52655682Smarkm return; 52755682Smarkm 52855682Smarkm needstats = f_inode || f_longform || f_size; 52955682Smarkm flen = 0; 53090926Snectar btotal = 0; 53190926Snectar initmax = getenv("LS_COLWIDTHS"); 53290926Snectar /* Fields match -lios order. New ones should be added at the end. */ 53390926Snectar maxlattr = maxblock = maxinode = maxlen = maxnlink = 53490926Snectar maxuser = maxgroup = maxflags = maxsize = 0; 53590926Snectar if (initmax != NULL && *initmax != '\0') { 536102644Snectar char *initmax2, *jinitmax; 537102644Snectar int ninitmax; 53890926Snectar 53990926Snectar /* Fill-in "::" as "0:0:0" for the sake of scanf. */ 54090926Snectar jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); 54190926Snectar if (jinitmax == NULL) 54290926Snectar err(1, NULL); 54355682Smarkm if (*initmax == ':') 54490926Snectar strcpy(initmax2, "0:"), initmax2 += 2; 545178825Sdfr else 546178825Sdfr *initmax2++ = *initmax, *initmax2 = '\0'; 547178825Sdfr for (initmax++; *initmax != '\0'; initmax++) { 54890926Snectar if (initmax[-1] == ':' && initmax[0] == ':') { 54990926Snectar *initmax2++ = '0'; 55055682Smarkm *initmax2++ = initmax[0]; 55155682Smarkm initmax2[1] = '\0'; 55255682Smarkm } else { 55355682Smarkm *initmax2++ = initmax[0]; 554233294Sstas initmax2[1] = '\0'; 55555682Smarkm } 55655682Smarkm } 55755682Smarkm if (initmax2[-1] == ':') 558233294Sstas strcpy(initmax2, "0"); 55955682Smarkm 56055682Smarkm ninitmax = sscanf(jinitmax, 56155682Smarkm " %lu : %lu : %lu : %i : %i : %i : %llu : %lu : %lu ", 562233294Sstas &maxinode, &maxblock, &maxnlink, &maxuser, 56355682Smarkm &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr); 56455682Smarkm f_notabs = 1; 56555682Smarkm switch (ninitmax) { 56672445Sassar case 0: 56755682Smarkm maxinode = 0; 56855682Smarkm /* fall through */ 56955682Smarkm case 1: 57055682Smarkm maxblock = 0; 57155682Smarkm /* fall through */ 57255682Smarkm case 2: 57355682Smarkm maxnlink = 0; 57455682Smarkm /* fall through */ 57555682Smarkm case 3: 57655682Smarkm maxuser = 0; 57755682Smarkm /* fall through */ 57855682Smarkm case 4: 57955682Smarkm maxgroup = 0; 58055682Smarkm /* fall through */ 58155682Smarkm case 5: 58255682Smarkm maxflags = 0; 58355682Smarkm /* fall through */ 58455682Smarkm case 6: 58555682Smarkm maxsize = 0; 58655682Smarkm /* fall through */ 58755682Smarkm case 7: 58855682Smarkm maxlen = 0; 58955682Smarkm /* fall through */ 59055682Smarkm case 8: 59155682Smarkm maxlattr = 0; 59255682Smarkm /* fall through */ 59355682Smarkm#ifdef COLORLS 59455682Smarkm if (!f_color) 59555682Smarkm#endif 59655682Smarkm f_notabs = 0; 59755682Smarkm /* fall through */ 59855682Smarkm default: 59955682Smarkm break; 60055682Smarkm } 60155682Smarkm maxinode = makenines(maxinode); 602178825Sdfr maxblock = makenines(maxblock); 603178825Sdfr maxnlink = makenines(maxnlink); 604178825Sdfr maxsize = makenines(maxsize); 605233294Sstas } 60690926Snectar if (f_lomac) 60790926Snectar lomac_start(); 60855682Smarkm bcfile = 0; 60955682Smarkm flags = NULL; 61055682Smarkm for (cur = list, entries = 0; cur; cur = cur->fts_link) { 61155682Smarkm if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 61255682Smarkm warnx("%s: %s", 61355682Smarkm cur->fts_name, strerror(cur->fts_errno)); 61455682Smarkm cur->fts_number = NO_PRINT; 61555682Smarkm rval = 1; 61655682Smarkm continue; 61755682Smarkm } 61855682Smarkm /* 61955682Smarkm * P is NULL if list is the argv list, to which different rules 62055682Smarkm * apply. 62155682Smarkm */ 62255682Smarkm if (p == NULL) { 62355682Smarkm /* Directories will be displayed later. */ 62455682Smarkm if (cur->fts_info == FTS_D && !f_listdir) { 62555682Smarkm cur->fts_number = NO_PRINT; 62655682Smarkm continue; 62755682Smarkm } 62855682Smarkm } else { 62955682Smarkm /* Only display dot file if -a/-A set. */ 63055682Smarkm if (cur->fts_name[0] == '.' && !f_listdot) { 63155682Smarkm cur->fts_number = NO_PRINT; 63255682Smarkm continue; 63355682Smarkm } 63455682Smarkm } 63555682Smarkm if (cur->fts_namelen > maxlen) 636233294Sstas maxlen = cur->fts_namelen; 63772445Sassar if (f_octal || f_octal_escape) { 63872445Sassar u_long t = len_octal(cur->fts_name, cur->fts_namelen); 63990926Snectar 64072445Sassar if (t > maxlen) 64172445Sassar maxlen = t; 64272445Sassar } 64372445Sassar if (needstats) { 64472445Sassar sp = cur->fts_statp; 64572445Sassar if (sp->st_blocks > maxblock) 64672445Sassar maxblock = sp->st_blocks; 64772445Sassar if (sp->st_ino > maxinode) 64872445Sassar maxinode = sp->st_ino; 64972445Sassar if (sp->st_nlink > maxnlink) 650178825Sdfr maxnlink = sp->st_nlink; 651178825Sdfr if (sp->st_size > maxsize) 65272445Sassar maxsize = sp->st_size; 65390926Snectar 65490926Snectar btotal += sp->st_blocks; 65590926Snectar if (f_longform) { 65690926Snectar if (f_numericonly) { 65790926Snectar (void)snprintf(nuser, sizeof(nuser), 65890926Snectar "%u", sp->st_uid); 65990926Snectar (void)snprintf(ngroup, sizeof(ngroup), 66090926Snectar "%u", sp->st_gid); 66190926Snectar user = nuser; 66290926Snectar group = ngroup; 663233294Sstas } else { 66490926Snectar user = user_from_uid(sp->st_uid, 0); 66572445Sassar group = group_from_gid(sp->st_gid, 0); 66672445Sassar } 66772445Sassar if ((ulen = strlen(user)) > maxuser) 66872445Sassar maxuser = ulen; 66990926Snectar if ((glen = strlen(group)) > maxgroup) 670233294Sstas maxgroup = glen; 67190926Snectar if (f_flags) { 67290926Snectar flags = fflagstostr(sp->st_flags); 67390926Snectar if (flags != NULL && *flags == '\0') { 67472445Sassar free(flags); 67572445Sassar flags = strdup("-"); 67672445Sassar } 67772445Sassar if (flags == NULL) 67872445Sassar err(1, NULL); 67972445Sassar flen = strlen(flags); 68072445Sassar if (flen > (size_t)maxflags) 68190926Snectar maxflags = flen; 68272445Sassar } else 68372445Sassar flen = 0; 684233294Sstas lattr = NULL; 68572445Sassar if (f_lomac) { 68672445Sassar lattr = get_lattr(cur); 68772445Sassar lattrlen = strlen(lattr); 68872445Sassar if (lattrlen > maxlattr) 68990926Snectar maxlattr = lattrlen; 690233294Sstas } else 69190926Snectar lattrlen = 0; 69290926Snectar 69390926Snectar if ((np = malloc(sizeof(NAMES) + lattrlen + 69472445Sassar ulen + glen + flen + 4)) == NULL) 69572445Sassar err(1, NULL); 69672445Sassar 69772445Sassar np->user = &np->data[0]; 69872445Sassar (void)strcpy(np->user, user); 69972445Sassar np->group = &np->data[ulen + 1]; 70072445Sassar (void)strcpy(np->group, group); 70172445Sassar 70272445Sassar if (S_ISCHR(sp->st_mode) || 70372445Sassar S_ISBLK(sp->st_mode)) 70472445Sassar bcfile = 1; 70555682Smarkm 70690926Snectar if (f_flags) { 70790926Snectar np->flags = &np->data[ulen + glen + 2]; 70890926Snectar (void)strcpy(np->flags, flags); 70990926Snectar free(flags); 71090926Snectar } 71190926Snectar if (f_lomac) { 71290926Snectar np->lattr = &np->data[ulen + glen + 2 713233294Sstas + (f_flags ? flen + 1 : 0)]; 71490926Snectar (void)strcpy(np->lattr, lattr); 71590926Snectar free(lattr); 71690926Snectar } 71790926Snectar cur->fts_pointer = np; 71890926Snectar } 71990926Snectar } 72090926Snectar ++entries; 72190926Snectar } 72290926Snectar 72390926Snectar if (!entries) 72490926Snectar return; 72590926Snectar 72690926Snectar d.list = list; 72772445Sassar d.entries = entries; 72872445Sassar d.maxlen = maxlen; 72972445Sassar if (needstats) { 73090926Snectar d.bcfile = bcfile; 73190926Snectar d.btotal = btotal; 732102644Snectar (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 73355682Smarkm d.s_block = strlen(buf); 73455682Smarkm d.s_flags = maxflags; 73555682Smarkm d.s_lattr = maxlattr; 73655682Smarkm d.s_group = maxgroup; 73755682Smarkm (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 73855682Smarkm d.s_inode = strlen(buf); 73955682Smarkm (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 74055682Smarkm d.s_nlink = strlen(buf); 74155682Smarkm (void)snprintf(buf, sizeof(buf), "%qu", maxsize); 74255682Smarkm d.s_size = strlen(buf); 74355682Smarkm d.s_user = maxuser; 74455682Smarkm } 74590926Snectar printfcn(&d); 74690926Snectar output = 1; 74790926Snectar 74890926Snectar if (f_longform) 74990926Snectar for (cur = list; cur; cur = cur->fts_link) 75090926Snectar free(cur->fts_pointer); 75190926Snectar if (f_lomac) 75290926Snectar lomac_stop(); 75390926Snectar} 75490926Snectar 75590926Snectar/* 75690926Snectar * Ordering for mastercmp: 75790926Snectar * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 75890926Snectar * as larger than directories. Within either group, use the sort function. 75990926Snectar * All other levels use the sort function. Error entries remain unsorted. 76090926Snectar */ 761102644Snectarstatic int 76255682Smarkmmastercmp(const FTSENT **a, const FTSENT **b) 76355682Smarkm{ 76455682Smarkm int a_info, b_info; 76555682Smarkm 76655682Smarkm a_info = (*a)->fts_info; 76755682Smarkm if (a_info == FTS_ERR) 768178825Sdfr return (0); 76955682Smarkm b_info = (*b)->fts_info; 77055682Smarkm if (b_info == FTS_ERR) 771102644Snectar return (0); 772102644Snectar 77355682Smarkm if (a_info == FTS_NS || b_info == FTS_NS) 77455682Smarkm return (namecmp(*a, *b)); 77555682Smarkm 77655682Smarkm if (a_info != b_info && 77790926Snectar (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { 77890926Snectar if (a_info == FTS_D) 77955682Smarkm return (1); 78055682Smarkm if (b_info == FTS_D) 781102644Snectar return (-1); 78255682Smarkm } 78355682Smarkm return (sortfcn(*a, *b)); 784102644Snectar} 78555682Smarkm 78655682Smarkm/* 787178825Sdfr * Makenines() returns (10**n)-1. This is useful for converting a width 788178825Sdfr * into a number that wide in decimal. 789102644Snectar */ 79055682Smarkmstatic u_quad_t 79155682Smarkmmakenines(u_long n) 792102644Snectar{ 79355682Smarkm u_long i; 79455682Smarkm u_quad_t reg; 79555682Smarkm 79655682Smarkm reg = 1; 797102644Snectar /* Use a loop instead of pow(), since all values of n are small. */ 79855682Smarkm for (i = 0; i < n; i++) 79955682Smarkm reg *= 10; 80090926Snectar reg--; 80190926Snectar 80290926Snectar return reg; 80390926Snectar} 80490926Snectar