1 1.79 simonb /* $NetBSD: ls.c,v 1.79 2024/12/11 12:56:31 simonb Exp $ */ 2 1.14 cgd 3 1.1 cgd /* 4 1.12 mycroft * Copyright (c) 1989, 1993, 1994 5 1.12 mycroft * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Michael Fischbein. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.50 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.19 christos #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.64 lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ 38 1.64 lukem The Regents of the University of California. All rights reserved."); 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd #ifndef lint 42 1.14 cgd #if 0 43 1.14 cgd static char sccsid[] = "@(#)ls.c 8.7 (Berkeley) 8/5/94"; 44 1.14 cgd #else 45 1.79 simonb __RCSID("$NetBSD: ls.c,v 1.79 2024/12/11 12:56:31 simonb Exp $"); 46 1.14 cgd #endif 47 1.1 cgd #endif /* not lint */ 48 1.1 cgd 49 1.67 rmind #include <sys/param.h> 50 1.7 mycroft #include <sys/types.h> 51 1.1 cgd #include <sys/stat.h> 52 1.1 cgd #include <sys/ioctl.h> 53 1.12 mycroft 54 1.1 cgd #include <dirent.h> 55 1.12 mycroft #include <err.h> 56 1.12 mycroft #include <errno.h> 57 1.7 mycroft #include <fts.h> 58 1.39 tron #include <locale.h> 59 1.12 mycroft #include <stdio.h> 60 1.7 mycroft #include <stdlib.h> 61 1.1 cgd #include <string.h> 62 1.12 mycroft #include <unistd.h> 63 1.33 christos #include <termios.h> 64 1.21 christos #include <pwd.h> 65 1.21 christos #include <grp.h> 66 1.63 he #include <util.h> 67 1.12 mycroft 68 1.1 cgd #include "ls.h" 69 1.7 mycroft #include "extern.h" 70 1.1 cgd 71 1.43 lukem static void display(FTSENT *, FTSENT *); 72 1.43 lukem static int mastercmp(const FTSENT **, const FTSENT **); 73 1.43 lukem static void traverse(int, char **, int); 74 1.7 mycroft 75 1.43 lukem static void (*printfcn)(DISPLAY *); 76 1.43 lukem static int (*sortfcn)(const FTSENT *, const FTSENT *); 77 1.7 mycroft 78 1.12 mycroft #define BY_NAME 0 79 1.12 mycroft #define BY_SIZE 1 80 1.8 mycroft #define BY_TIME 2 81 1.8 mycroft 82 1.7 mycroft long blocksize; /* block size units */ 83 1.1 cgd int termwidth = 80; /* default terminal width */ 84 1.8 mycroft int sortkey = BY_NAME; 85 1.37 simonb int rval = EXIT_SUCCESS; /* exit value - set if error encountered */ 86 1.1 cgd 87 1.1 cgd /* flags */ 88 1.1 cgd int f_accesstime; /* use time of last access */ 89 1.1 cgd int f_column; /* columnated format */ 90 1.24 lukem int f_columnacross; /* columnated format, sorted across */ 91 1.7 mycroft int f_flags; /* show flags associated with a file */ 92 1.46 grant int f_grouponly; /* long listing without owner */ 93 1.53 grant int f_humanize; /* humanize the size field */ 94 1.76 rin int f_commas; /* separate size field with comma */ 95 1.1 cgd int f_inode; /* print inode */ 96 1.1 cgd int f_listdir; /* list actual directory, not contents */ 97 1.1 cgd int f_listdot; /* list files beginning with . */ 98 1.1 cgd int f_longform; /* long listing format */ 99 1.1 cgd int f_nonprint; /* show unprintables as ? */ 100 1.1 cgd int f_nosort; /* don't sort output */ 101 1.24 lukem int f_numericonly; /* don't convert uid/gid to name */ 102 1.52 jschauma int f_octal; /* print octal escapes for nongraphic characters */ 103 1.52 jschauma int f_octal_escape; /* like f_octal but use C escapes if possible */ 104 1.1 cgd int f_recursive; /* ls subdirectories also */ 105 1.1 cgd int f_reversesort; /* reverse whatever sort is used */ 106 1.1 cgd int f_sectime; /* print the real time for all files */ 107 1.1 cgd int f_singlecol; /* use single column output */ 108 1.1 cgd int f_size; /* list size in short listing */ 109 1.1 cgd int f_statustime; /* use time of last mode change */ 110 1.35 kleink int f_stream; /* stream format */ 111 1.1 cgd int f_type; /* add type character for non-regular files */ 112 1.36 kleink int f_typedir; /* add type character for directories */ 113 1.13 mycroft int f_whiteout; /* show whiteout entries */ 114 1.71 christos int f_fullpath; /* print full pathname, not filename */ 115 1.71 christos int f_leafonly; /* when recursing, print leaf names only */ 116 1.1 cgd 117 1.69 joerg __dead static void 118 1.69 joerg usage(void) 119 1.69 joerg { 120 1.69 joerg 121 1.69 joerg (void)fprintf(stderr, 122 1.72 christos "usage: %s [-1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx] [file ...]\n", 123 1.69 joerg getprogname()); 124 1.69 joerg exit(EXIT_FAILURE); 125 1.69 joerg /* NOTREACHED */ 126 1.69 joerg } 127 1.69 joerg 128 1.7 mycroft int 129 1.43 lukem ls_main(int argc, char *argv[]) 130 1.1 cgd { 131 1.7 mycroft static char dot[] = ".", *dotav[] = { dot, NULL }; 132 1.1 cgd struct winsize win; 133 1.49 simonb int ch, fts_options; 134 1.7 mycroft int kflag = 0; 135 1.27 mycroft const char *p; 136 1.39 tron 137 1.56 hira setprogname(argv[0]); 138 1.62 christos (void)setlocale(LC_ALL, ""); 139 1.7 mycroft 140 1.7 mycroft /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 141 1.7 mycroft if (isatty(STDOUT_FILENO)) { 142 1.57 jschauma if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && 143 1.16 jtc win.ws_col > 0) 144 1.1 cgd termwidth = win.ws_col; 145 1.7 mycroft f_column = f_nonprint = 1; 146 1.1 cgd } else 147 1.1 cgd f_singlecol = 1; 148 1.1 cgd 149 1.7 mycroft /* Root is -A automatically. */ 150 1.1 cgd if (!getuid()) 151 1.1 cgd f_listdot = 1; 152 1.1 cgd 153 1.7 mycroft fts_options = FTS_PHYSICAL; 154 1.73 mlelstv while ((ch = getopt(argc, argv, "1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx")) 155 1.72 christos != -1) { 156 1.1 cgd switch (ch) { 157 1.1 cgd /* 158 1.35 kleink * The -1, -C, -l, -m and -x options all override each other so 159 1.24 lukem * shell aliasing works correctly. 160 1.1 cgd */ 161 1.1 cgd case '1': 162 1.1 cgd f_singlecol = 1; 163 1.35 kleink f_column = f_columnacross = f_longform = f_stream = 0; 164 1.1 cgd break; 165 1.1 cgd case 'C': 166 1.1 cgd f_column = 1; 167 1.35 kleink f_columnacross = f_longform = f_singlecol = f_stream = 168 1.35 kleink 0; 169 1.1 cgd break; 170 1.46 grant case 'g': 171 1.48 kleink if (f_grouponly != -1) 172 1.48 kleink f_grouponly = 1; 173 1.48 kleink f_longform = 1; 174 1.48 kleink f_column = f_columnacross = f_singlecol = f_stream = 0; 175 1.48 kleink break; 176 1.1 cgd case 'l': 177 1.1 cgd f_longform = 1; 178 1.35 kleink f_column = f_columnacross = f_singlecol = f_stream = 0; 179 1.48 kleink /* Never let -g take precedence over -l. */ 180 1.48 kleink f_grouponly = -1; 181 1.35 kleink break; 182 1.35 kleink case 'm': 183 1.35 kleink f_stream = 1; 184 1.35 kleink f_column = f_columnacross = f_longform = f_singlecol = 185 1.35 kleink 0; 186 1.24 lukem break; 187 1.24 lukem case 'x': 188 1.24 lukem f_columnacross = 1; 189 1.35 kleink f_column = f_longform = f_singlecol = f_stream = 0; 190 1.1 cgd break; 191 1.7 mycroft /* The -c and -u options override each other. */ 192 1.1 cgd case 'c': 193 1.1 cgd f_statustime = 1; 194 1.1 cgd f_accesstime = 0; 195 1.1 cgd break; 196 1.1 cgd case 'u': 197 1.1 cgd f_accesstime = 1; 198 1.1 cgd f_statustime = 0; 199 1.1 cgd break; 200 1.1 cgd case 'F': 201 1.1 cgd f_type = 1; 202 1.1 cgd break; 203 1.1 cgd case 'L': 204 1.7 mycroft fts_options &= ~FTS_PHYSICAL; 205 1.7 mycroft fts_options |= FTS_LOGICAL; 206 1.1 cgd break; 207 1.1 cgd case 'R': 208 1.1 cgd f_recursive = 1; 209 1.1 cgd break; 210 1.74 wiz case 'f': 211 1.74 wiz f_nosort = 1; 212 1.74 wiz /* FALLTHROUGH */ 213 1.1 cgd case 'a': 214 1.7 mycroft fts_options |= FTS_SEEDOT; 215 1.1 cgd /* FALLTHROUGH */ 216 1.1 cgd case 'A': 217 1.1 cgd f_listdot = 1; 218 1.1 cgd break; 219 1.58 jschauma /* The -B option turns off the -b, -q and -w options. */ 220 1.52 jschauma case 'B': 221 1.52 jschauma f_nonprint = 0; 222 1.52 jschauma f_octal = 1; 223 1.52 jschauma f_octal_escape = 0; 224 1.52 jschauma break; 225 1.58 jschauma /* The -b option turns off the -B, -q and -w options. */ 226 1.51 jschauma case 'b': 227 1.51 jschauma f_nonprint = 0; 228 1.52 jschauma f_octal = 0; 229 1.52 jschauma f_octal_escape = 1; 230 1.51 jschauma break; 231 1.7 mycroft /* The -d option turns off the -R option. */ 232 1.1 cgd case 'd': 233 1.1 cgd f_listdir = 1; 234 1.7 mycroft f_recursive = 0; 235 1.1 cgd break; 236 1.1 cgd case 'i': 237 1.1 cgd f_inode = 1; 238 1.1 cgd break; 239 1.1 cgd case 'k': 240 1.7 mycroft blocksize = 1024; 241 1.7 mycroft kflag = 1; 242 1.60 christos f_humanize = 0; 243 1.1 cgd break; 244 1.58 jschauma /* The -h option forces all sizes to be measured in bytes. */ 245 1.53 grant case 'h': 246 1.53 grant f_humanize = 1; 247 1.60 christos kflag = 0; 248 1.68 erh f_commas = 0; 249 1.68 erh break; 250 1.68 erh case 'M': 251 1.68 erh f_humanize = 0; 252 1.68 erh f_commas = 1; 253 1.53 grant break; 254 1.24 lukem case 'n': 255 1.24 lukem f_numericonly = 1; 256 1.65 lukem f_longform = 1; 257 1.65 lukem f_column = f_columnacross = f_singlecol = f_stream = 0; 258 1.24 lukem break; 259 1.71 christos case 'O': 260 1.71 christos f_leafonly = 1; 261 1.71 christos break; 262 1.7 mycroft case 'o': 263 1.7 mycroft f_flags = 1; 264 1.7 mycroft break; 265 1.71 christos case 'P': 266 1.71 christos f_fullpath = 1; 267 1.71 christos break; 268 1.36 kleink case 'p': 269 1.36 kleink f_typedir = 1; 270 1.36 kleink break; 271 1.58 jschauma /* The -q option turns off the -B, -b and -w options. */ 272 1.1 cgd case 'q': 273 1.1 cgd f_nonprint = 1; 274 1.52 jschauma f_octal = 0; 275 1.52 jschauma f_octal_escape = 0; 276 1.1 cgd break; 277 1.1 cgd case 'r': 278 1.1 cgd f_reversesort = 1; 279 1.1 cgd break; 280 1.8 mycroft case 'S': 281 1.8 mycroft sortkey = BY_SIZE; 282 1.8 mycroft break; 283 1.1 cgd case 's': 284 1.1 cgd f_size = 1; 285 1.1 cgd break; 286 1.1 cgd case 'T': 287 1.1 cgd f_sectime = 1; 288 1.1 cgd break; 289 1.1 cgd case 't': 290 1.8 mycroft sortkey = BY_TIME; 291 1.1 cgd break; 292 1.13 mycroft case 'W': 293 1.13 mycroft f_whiteout = 1; 294 1.52 jschauma break; 295 1.58 jschauma /* The -w option turns off the -B, -b and -q options. */ 296 1.52 jschauma case 'w': 297 1.52 jschauma f_nonprint = 0; 298 1.52 jschauma f_octal = 0; 299 1.52 jschauma f_octal_escape = 0; 300 1.13 mycroft break; 301 1.72 christos case 'X': 302 1.72 christos fts_options |= FTS_XDEV; 303 1.72 christos break; 304 1.1 cgd default: 305 1.1 cgd case '?': 306 1.1 cgd usage(); 307 1.1 cgd } 308 1.1 cgd } 309 1.1 cgd argc -= optind; 310 1.1 cgd argv += optind; 311 1.48 kleink 312 1.57 jschauma if (f_column || f_columnacross || f_stream) { 313 1.57 jschauma if ((p = getenv("COLUMNS")) != NULL) 314 1.57 jschauma termwidth = atoi(p); 315 1.57 jschauma } 316 1.57 jschauma 317 1.48 kleink /* 318 1.48 kleink * If both -g and -l options, let -l take precedence. 319 1.48 kleink */ 320 1.48 kleink if (f_grouponly == -1) 321 1.48 kleink f_grouponly = 0; 322 1.1 cgd 323 1.7 mycroft /* 324 1.36 kleink * If not -F, -i, -l, -p, -S, -s or -t options, don't require stat 325 1.7 mycroft * information. 326 1.7 mycroft */ 327 1.36 kleink if (!f_inode && !f_longform && !f_size && !f_type && !f_typedir && 328 1.8 mycroft sortkey == BY_NAME) 329 1.7 mycroft fts_options |= FTS_NOSTAT; 330 1.7 mycroft 331 1.7 mycroft /* 332 1.7 mycroft * If not -F, -d or -l options, follow any symbolic links listed on 333 1.7 mycroft * the command line. 334 1.7 mycroft */ 335 1.7 mycroft if (!f_longform && !f_listdir && !f_type) 336 1.7 mycroft fts_options |= FTS_COMFOLLOW; 337 1.13 mycroft 338 1.13 mycroft /* 339 1.15 jtc * If -W, show whiteout entries 340 1.13 mycroft */ 341 1.13 mycroft #ifdef FTS_WHITEOUT 342 1.13 mycroft if (f_whiteout) 343 1.13 mycroft fts_options |= FTS_WHITEOUT; 344 1.13 mycroft #endif 345 1.1 cgd 346 1.70 abs /* If -i, -l, or -s, figure out block size. */ 347 1.45 simonb if (f_inode || f_longform || f_size) { 348 1.9 cgd if (!kflag) 349 1.49 simonb (void)getbsize(NULL, &blocksize); 350 1.79 simonb blocksize /= POSIX_BLOCK_SIZE; 351 1.7 mycroft } 352 1.1 cgd 353 1.7 mycroft /* Select a sort function. */ 354 1.1 cgd if (f_reversesort) { 355 1.8 mycroft switch (sortkey) { 356 1.8 mycroft case BY_NAME: 357 1.1 cgd sortfcn = revnamecmp; 358 1.8 mycroft break; 359 1.8 mycroft case BY_SIZE: 360 1.8 mycroft sortfcn = revsizecmp; 361 1.8 mycroft break; 362 1.8 mycroft case BY_TIME: 363 1.8 mycroft if (f_accesstime) 364 1.8 mycroft sortfcn = revacccmp; 365 1.8 mycroft else if (f_statustime) 366 1.8 mycroft sortfcn = revstatcmp; 367 1.12 mycroft else /* Use modification time. */ 368 1.8 mycroft sortfcn = revmodcmp; 369 1.8 mycroft break; 370 1.8 mycroft } 371 1.1 cgd } else { 372 1.8 mycroft switch (sortkey) { 373 1.8 mycroft case BY_NAME: 374 1.1 cgd sortfcn = namecmp; 375 1.8 mycroft break; 376 1.8 mycroft case BY_SIZE: 377 1.8 mycroft sortfcn = sizecmp; 378 1.8 mycroft break; 379 1.8 mycroft case BY_TIME: 380 1.8 mycroft if (f_accesstime) 381 1.8 mycroft sortfcn = acccmp; 382 1.8 mycroft else if (f_statustime) 383 1.8 mycroft sortfcn = statcmp; 384 1.12 mycroft else /* Use modification time. */ 385 1.8 mycroft sortfcn = modcmp; 386 1.8 mycroft break; 387 1.8 mycroft } 388 1.1 cgd } 389 1.1 cgd 390 1.7 mycroft /* Select a print function. */ 391 1.1 cgd if (f_singlecol) 392 1.1 cgd printfcn = printscol; 393 1.24 lukem else if (f_columnacross) 394 1.24 lukem printfcn = printacol; 395 1.1 cgd else if (f_longform) 396 1.1 cgd printfcn = printlong; 397 1.35 kleink else if (f_stream) 398 1.35 kleink printfcn = printstream; 399 1.1 cgd else 400 1.1 cgd printfcn = printcol; 401 1.1 cgd 402 1.7 mycroft if (argc) 403 1.7 mycroft traverse(argc, argv, fts_options); 404 1.7 mycroft else 405 1.7 mycroft traverse(1, dotav, fts_options); 406 1.62 christos return rval; 407 1.30 mycroft /* NOTREACHED */ 408 1.1 cgd } 409 1.1 cgd 410 1.7 mycroft static int output; /* If anything output. */ 411 1.1 cgd 412 1.7 mycroft /* 413 1.7 mycroft * Traverse() walks the logical directory structure specified by the argv list 414 1.7 mycroft * in the order specified by the mastercmp() comparison function. During the 415 1.7 mycroft * traversal it passes linked lists of structures to display() which represent 416 1.7 mycroft * a superset (may be exact set) of the files to be displayed. 417 1.7 mycroft */ 418 1.7 mycroft static void 419 1.43 lukem traverse(int argc, char *argv[], int options) 420 1.1 cgd { 421 1.12 mycroft FTS *ftsp; 422 1.12 mycroft FTSENT *p, *chp; 423 1.59 christos int ch_options, error; 424 1.7 mycroft 425 1.7 mycroft if ((ftsp = 426 1.7 mycroft fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 427 1.40 drochner err(EXIT_FAILURE, NULL); 428 1.7 mycroft 429 1.7 mycroft display(NULL, fts_children(ftsp, 0)); 430 1.59 christos if (f_listdir) { 431 1.62 christos (void)fts_close(ftsp); 432 1.7 mycroft return; 433 1.59 christos } 434 1.1 cgd 435 1.1 cgd /* 436 1.7 mycroft * If not recursing down this tree and don't need stat info, just get 437 1.7 mycroft * the names. 438 1.1 cgd */ 439 1.7 mycroft ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 440 1.1 cgd 441 1.12 mycroft while ((p = fts_read(ftsp)) != NULL) 442 1.12 mycroft switch (p->fts_info) { 443 1.7 mycroft case FTS_DC: 444 1.9 cgd warnx("%s: directory causes a cycle", p->fts_name); 445 1.7 mycroft break; 446 1.12 mycroft case FTS_DNR: 447 1.12 mycroft case FTS_ERR: 448 1.78 christos warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 449 1.37 simonb rval = EXIT_FAILURE; 450 1.12 mycroft break; 451 1.7 mycroft case FTS_D: 452 1.7 mycroft if (p->fts_level != FTS_ROOTLEVEL && 453 1.77 christos p->fts_name[0] == '.' && !f_listdot) { 454 1.77 christos (void)fts_set(ftsp, p, FTS_SKIP); 455 1.7 mycroft break; 456 1.77 christos } 457 1.1 cgd 458 1.7 mycroft /* 459 1.7 mycroft * If already output something, put out a newline as 460 1.7 mycroft * a separator. If multiple arguments, precede each 461 1.7 mycroft * directory with its name. 462 1.7 mycroft */ 463 1.71 christos if (!f_leafonly) { 464 1.71 christos if (output) 465 1.71 christos (void)printf("\n%s:\n", p->fts_path); 466 1.71 christos else if (argc > 1) { 467 1.71 christos (void)printf("%s:\n", p->fts_path); 468 1.71 christos output = 1; 469 1.71 christos } 470 1.7 mycroft } 471 1.1 cgd 472 1.12 mycroft chp = fts_children(ftsp, ch_options); 473 1.12 mycroft display(p, chp); 474 1.1 cgd 475 1.12 mycroft if (!f_recursive && chp != NULL) 476 1.7 mycroft (void)fts_set(ftsp, p, FTS_SKIP); 477 1.7 mycroft break; 478 1.1 cgd } 479 1.59 christos error = errno; 480 1.62 christos (void)fts_close(ftsp); 481 1.59 christos errno = error; 482 1.12 mycroft if (errno) 483 1.35 kleink err(EXIT_FAILURE, "fts_read"); 484 1.1 cgd } 485 1.1 cgd 486 1.7 mycroft /* 487 1.7 mycroft * Display() takes a linked list of FTSENT structures and passes the list 488 1.7 mycroft * along with any other necessary information to the print function. P 489 1.7 mycroft * points to the parent directory of the display list. 490 1.7 mycroft */ 491 1.7 mycroft static void 492 1.43 lukem display(FTSENT *p, FTSENT *list) 493 1.1 cgd { 494 1.7 mycroft struct stat *sp; 495 1.7 mycroft DISPLAY d; 496 1.12 mycroft FTSENT *cur; 497 1.7 mycroft NAMES *np; 498 1.79 simonb u_int64_t btotal; 499 1.66 lukem off_t maxsize; 500 1.66 lukem blkcnt_t maxblock; 501 1.62 christos ino_t maxinode; 502 1.66 lukem int maxmajor, maxminor; 503 1.66 lukem uint32_t maxnlink; 504 1.66 lukem int bcfile, entries, flen, glen, ulen, maxflags, maxgroup; 505 1.66 lukem unsigned int maxlen; 506 1.26 lukem int maxuser, needstats; 507 1.27 mycroft const char *user, *group; 508 1.41 enami char buf[21]; /* 64 bits == 20 digits, +1 for NUL */ 509 1.24 lukem char nuser[12], ngroup[12]; 510 1.26 lukem char *flags = NULL; 511 1.25 mycroft 512 1.7 mycroft /* 513 1.7 mycroft * If list is NULL there are two possibilities: that the parent 514 1.7 mycroft * directory p has no children, or that fts_children() returned an 515 1.12 mycroft * error. We ignore the error case since it will be replicated 516 1.12 mycroft * on the next call to fts_read() on the post-order visit to the 517 1.12 mycroft * directory p, and will be signalled in traverse(). 518 1.7 mycroft */ 519 1.12 mycroft if (list == NULL) 520 1.7 mycroft return; 521 1.1 cgd 522 1.7 mycroft needstats = f_inode || f_longform || f_size; 523 1.7 mycroft flen = 0; 524 1.26 lukem maxinode = maxnlink = 0; 525 1.7 mycroft bcfile = 0; 526 1.26 lukem maxuser = maxgroup = maxflags = maxlen = 0; 527 1.79 simonb btotal = maxblock = maxsize = 0; 528 1.23 mycroft maxmajor = maxminor = 0; 529 1.7 mycroft for (cur = list, entries = 0; cur; cur = cur->fts_link) { 530 1.7 mycroft if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 531 1.12 mycroft warnx("%s: %s", 532 1.12 mycroft cur->fts_name, strerror(cur->fts_errno)); 533 1.7 mycroft cur->fts_number = NO_PRINT; 534 1.37 simonb rval = EXIT_FAILURE; 535 1.7 mycroft continue; 536 1.7 mycroft } 537 1.1 cgd 538 1.7 mycroft /* 539 1.7 mycroft * P is NULL if list is the argv list, to which different rules 540 1.7 mycroft * apply. 541 1.7 mycroft */ 542 1.7 mycroft if (p == NULL) { 543 1.7 mycroft /* Directories will be displayed later. */ 544 1.7 mycroft if (cur->fts_info == FTS_D && !f_listdir) { 545 1.7 mycroft cur->fts_number = NO_PRINT; 546 1.1 cgd continue; 547 1.7 mycroft } 548 1.7 mycroft } else { 549 1.7 mycroft /* Only display dot file if -a/-A set. */ 550 1.7 mycroft if (cur->fts_name[0] == '.' && !f_listdot) { 551 1.7 mycroft cur->fts_number = NO_PRINT; 552 1.1 cgd continue; 553 1.7 mycroft } 554 1.7 mycroft } 555 1.7 mycroft if (cur->fts_namelen > maxlen) 556 1.7 mycroft maxlen = cur->fts_namelen; 557 1.7 mycroft if (needstats) { 558 1.7 mycroft sp = cur->fts_statp; 559 1.7 mycroft if (sp->st_blocks > maxblock) 560 1.7 mycroft maxblock = sp->st_blocks; 561 1.7 mycroft if (sp->st_ino > maxinode) 562 1.7 mycroft maxinode = sp->st_ino; 563 1.7 mycroft if (sp->st_nlink > maxnlink) 564 1.7 mycroft maxnlink = sp->st_nlink; 565 1.7 mycroft if (sp->st_size > maxsize) 566 1.7 mycroft maxsize = sp->st_size; 567 1.23 mycroft if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) { 568 1.23 mycroft bcfile = 1; 569 1.23 mycroft if (major(sp->st_rdev) > maxmajor) 570 1.23 mycroft maxmajor = major(sp->st_rdev); 571 1.23 mycroft if (minor(sp->st_rdev) > maxminor) 572 1.23 mycroft maxminor = minor(sp->st_rdev); 573 1.23 mycroft } 574 1.7 mycroft 575 1.7 mycroft btotal += sp->st_blocks; 576 1.7 mycroft if (f_longform) { 577 1.41 enami if (f_numericonly || 578 1.41 enami (user = user_from_uid(sp->st_uid, 0)) == 579 1.41 enami NULL) { 580 1.29 mycroft (void)snprintf(nuser, sizeof(nuser), 581 1.29 mycroft "%u", sp->st_uid); 582 1.41 enami user = nuser; 583 1.41 enami } 584 1.41 enami if (f_numericonly || 585 1.41 enami (group = group_from_gid(sp->st_gid, 0)) == 586 1.41 enami NULL) { 587 1.29 mycroft (void)snprintf(ngroup, sizeof(ngroup), 588 1.29 mycroft "%u", sp->st_gid); 589 1.24 lukem group = ngroup; 590 1.24 lukem } 591 1.7 mycroft if ((ulen = strlen(user)) > maxuser) 592 1.7 mycroft maxuser = ulen; 593 1.7 mycroft if ((glen = strlen(group)) > maxgroup) 594 1.7 mycroft maxgroup = glen; 595 1.7 mycroft if (f_flags) { 596 1.12 mycroft flags = 597 1.62 christos flags_to_string((u_long)sp->st_flags, "-"); 598 1.7 mycroft if ((flen = strlen(flags)) > maxflags) 599 1.7 mycroft maxflags = flen; 600 1.7 mycroft } else 601 1.7 mycroft flen = 0; 602 1.7 mycroft 603 1.7 mycroft if ((np = malloc(sizeof(NAMES) + 604 1.61 elad ulen + glen + flen + 2)) == NULL) 605 1.40 drochner err(EXIT_FAILURE, NULL); 606 1.7 mycroft 607 1.7 mycroft np->user = &np->data[0]; 608 1.7 mycroft (void)strcpy(np->user, user); 609 1.7 mycroft np->group = &np->data[ulen + 1]; 610 1.7 mycroft (void)strcpy(np->group, group); 611 1.7 mycroft 612 1.7 mycroft if (f_flags) { 613 1.7 mycroft np->flags = &np->data[ulen + glen + 2]; 614 1.7 mycroft (void)strcpy(np->flags, flags); 615 1.63 he free(flags); 616 1.7 mycroft } 617 1.7 mycroft cur->fts_pointer = np; 618 1.7 mycroft } 619 1.1 cgd } 620 1.7 mycroft ++entries; 621 1.1 cgd } 622 1.1 cgd 623 1.7 mycroft if (!entries) 624 1.1 cgd return; 625 1.7 mycroft 626 1.7 mycroft d.list = list; 627 1.7 mycroft d.entries = entries; 628 1.7 mycroft d.maxlen = maxlen; 629 1.7 mycroft if (needstats) { 630 1.7 mycroft d.btotal = btotal; 631 1.55 simonb if (f_humanize) { 632 1.55 simonb d.s_block = 4; /* min buf length for humanize_number */ 633 1.55 simonb } else { 634 1.75 dholland (void)snprintf(buf, sizeof(buf), "%lld", 635 1.53 grant (long long)howmany(maxblock, blocksize)); 636 1.53 grant d.s_block = strlen(buf); 637 1.68 erh if (f_commas) /* allow for commas before every third digit */ 638 1.68 erh d.s_block += (d.s_block - 1) / 3; 639 1.53 grant } 640 1.7 mycroft d.s_flags = maxflags; 641 1.7 mycroft d.s_group = maxgroup; 642 1.62 christos (void)snprintf(buf, sizeof(buf), "%llu", 643 1.62 christos (unsigned long long)maxinode); 644 1.7 mycroft d.s_inode = strlen(buf); 645 1.26 lukem (void)snprintf(buf, sizeof(buf), "%u", maxnlink); 646 1.7 mycroft d.s_nlink = strlen(buf); 647 1.53 grant if (f_humanize) { 648 1.54 simonb d.s_size = 4; /* min buf length for humanize_number */ 649 1.53 grant } else { 650 1.75 dholland (void)snprintf(buf, sizeof(buf), "%lld", 651 1.54 simonb (long long)maxsize); 652 1.53 grant d.s_size = strlen(buf); 653 1.68 erh if (f_commas) /* allow for commas before every third digit */ 654 1.68 erh d.s_size += (d.s_size - 1) / 3; 655 1.53 grant } 656 1.7 mycroft d.s_user = maxuser; 657 1.23 mycroft if (bcfile) { 658 1.75 dholland (void)snprintf(buf, sizeof(buf), "%d", maxmajor); 659 1.23 mycroft d.s_major = strlen(buf); 660 1.75 dholland (void)snprintf(buf, sizeof(buf), "%d", maxminor); 661 1.23 mycroft d.s_minor = strlen(buf); 662 1.23 mycroft if (d.s_major + d.s_minor + 2 > d.s_size) 663 1.23 mycroft d.s_size = d.s_major + d.s_minor + 2; 664 1.23 mycroft else if (d.s_size - d.s_minor - 2 > d.s_major) 665 1.23 mycroft d.s_major = d.s_size - d.s_minor - 2; 666 1.23 mycroft } else { 667 1.23 mycroft d.s_major = 0; 668 1.23 mycroft d.s_minor = 0; 669 1.23 mycroft } 670 1.7 mycroft } 671 1.7 mycroft 672 1.7 mycroft printfcn(&d); 673 1.7 mycroft output = 1; 674 1.7 mycroft 675 1.7 mycroft if (f_longform) 676 1.7 mycroft for (cur = list; cur; cur = cur->fts_link) 677 1.7 mycroft free(cur->fts_pointer); 678 1.1 cgd } 679 1.1 cgd 680 1.7 mycroft /* 681 1.7 mycroft * Ordering for mastercmp: 682 1.7 mycroft * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 683 1.7 mycroft * as larger than directories. Within either group, use the sort function. 684 1.7 mycroft * All other levels use the sort function. Error entries remain unsorted. 685 1.7 mycroft */ 686 1.7 mycroft static int 687 1.43 lukem mastercmp(const FTSENT **a, const FTSENT **b) 688 1.1 cgd { 689 1.12 mycroft int a_info, b_info; 690 1.1 cgd 691 1.7 mycroft a_info = (*a)->fts_info; 692 1.7 mycroft if (a_info == FTS_ERR) 693 1.7 mycroft return (0); 694 1.7 mycroft b_info = (*b)->fts_info; 695 1.7 mycroft if (b_info == FTS_ERR) 696 1.7 mycroft return (0); 697 1.7 mycroft 698 1.31 thorpej if (a_info == FTS_NS || b_info == FTS_NS) { 699 1.17 mycroft if (b_info != FTS_NS) 700 1.17 mycroft return (1); 701 1.17 mycroft else if (a_info != FTS_NS) 702 1.17 mycroft return (-1); 703 1.17 mycroft else 704 1.18 mycroft return (namecmp(*a, *b)); 705 1.31 thorpej } 706 1.7 mycroft 707 1.24 lukem if (a_info != b_info && !f_listdir && 708 1.24 lukem (*a)->fts_level == FTS_ROOTLEVEL) { 709 1.7 mycroft if (a_info == FTS_D) 710 1.7 mycroft return (1); 711 1.7 mycroft else if (b_info == FTS_D) 712 1.7 mycroft return (-1); 713 1.24 lukem } 714 1.24 lukem return (sortfcn(*a, *b)); 715 1.1 cgd } 716