1 /* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Michael Fischbein. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1989, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; 45 #endif /* not lint */ 46 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 #include <sys/ioctl.h> 50 51 #include <dirent.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <fts.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #include "ls.h" 61 #include "extern.h" 62 63 static void display __P((FTSENT *, FTSENT *)); 64 static int mastercmp __P((const FTSENT **, const FTSENT **)); 65 static void traverse __P((int, char **, int)); 66 67 static void (*printfcn) __P((DISPLAY *)); 68 static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); 69 70 long blocksize; /* block size units */ 71 int termwidth = 80; /* default terminal width */ 72 73 /* flags */ 74 int f_accesstime; /* use time of last access */ 75 int f_column; /* columnated format */ 76 int f_flags; /* show flags associated with a file */ 77 int f_inode; /* print inode */ 78 int f_listdir; /* list actual directory, not contents */ 79 int f_listdot; /* list files beginning with . */ 80 int f_longform; /* long listing format */ 81 int f_newline; /* if precede with newline */ 82 int f_nonprint; /* show unprintables as ? */ 83 int f_nosort; /* don't sort output */ 84 int f_recursive; /* ls subdirectories also */ 85 int f_reversesort; /* reverse whatever sort is used */ 86 int f_sectime; /* print the real time for all files */ 87 int f_singlecol; /* use single column output */ 88 int f_size; /* list size in short listing */ 89 int f_statustime; /* use time of last mode change */ 90 int f_dirname; /* if precede with directory name */ 91 int f_timesort; /* sort by time vice name */ 92 int f_type; /* add type character for non-regular files */ 93 94 int 95 main(argc, argv) 96 int argc; 97 char *argv[]; 98 { 99 static char dot[] = ".", *dotav[] = { dot, NULL }; 100 struct winsize win; 101 int ch, fts_options, notused; 102 char *p; 103 104 /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 105 if (isatty(STDOUT_FILENO)) { 106 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || 107 !win.ws_col) { 108 if ((p = getenv("COLUMNS")) != NULL) 109 termwidth = atoi(p); 110 } 111 else 112 termwidth = win.ws_col; 113 f_column = f_nonprint = 1; 114 } else 115 f_singlecol = 1; 116 117 /* Root is -A automatically. */ 118 if (!getuid()) 119 f_listdot = 1; 120 121 fts_options = FTS_PHYSICAL; 122 while ((ch = getopt(argc, argv, "1ACFLRTacdfgiloqrstu")) != EOF) { 123 switch (ch) { 124 /* 125 * The -1, -C and -l options all override each other so shell 126 * aliasing works right. 127 */ 128 case '1': 129 f_singlecol = 1; 130 f_column = f_longform = 0; 131 break; 132 case 'C': 133 f_column = 1; 134 f_longform = f_singlecol = 0; 135 break; 136 case 'l': 137 f_longform = 1; 138 f_column = f_singlecol = 0; 139 break; 140 /* The -c and -u options override each other. */ 141 case 'c': 142 f_statustime = 1; 143 f_accesstime = 0; 144 break; 145 case 'u': 146 f_accesstime = 1; 147 f_statustime = 0; 148 break; 149 case 'F': 150 f_type = 1; 151 break; 152 case 'L': 153 fts_options &= ~FTS_PHYSICAL; 154 fts_options |= FTS_LOGICAL; 155 break; 156 case 'R': 157 f_recursive = 1; 158 break; 159 case 'a': 160 fts_options |= FTS_SEEDOT; 161 /* FALLTHROUGH */ 162 case 'A': 163 f_listdot = 1; 164 break; 165 /* The -d option turns off the -R option. */ 166 case 'd': 167 f_listdir = 1; 168 f_recursive = 0; 169 break; 170 case 'f': 171 f_nosort = 1; 172 break; 173 case 'g': /* Compatibility with 4.3BSD. */ 174 break; 175 case 'i': 176 f_inode = 1; 177 break; 178 case 'o': 179 f_flags = 1; 180 break; 181 case 'q': 182 f_nonprint = 1; 183 break; 184 case 'r': 185 f_reversesort = 1; 186 break; 187 case 's': 188 f_size = 1; 189 break; 190 case 'T': 191 f_sectime = 1; 192 break; 193 case 't': 194 f_timesort = 1; 195 break; 196 default: 197 case '?': 198 usage(); 199 } 200 } 201 argc -= optind; 202 argv += optind; 203 204 /* 205 * If not -F, -i, -l, -s or -t options, don't require stat 206 * information. 207 */ 208 if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) 209 fts_options |= FTS_NOSTAT; 210 211 /* 212 * If not -F, -d or -l options, follow any symbolic links listed on 213 * the command line. 214 */ 215 if (!f_longform && !f_listdir && !f_type) 216 fts_options |= FTS_COMFOLLOW; 217 218 /* If -l or -s, figure out block size. */ 219 if (f_longform || f_size) { 220 (void)getbsize(¬used, &blocksize); 221 blocksize /= 512; 222 } 223 224 /* Select a sort function. */ 225 if (f_reversesort) { 226 if (!f_timesort) 227 sortfcn = revnamecmp; 228 else if (f_accesstime) 229 sortfcn = revacccmp; 230 else if (f_statustime) 231 sortfcn = revstatcmp; 232 else /* Use modification time. */ 233 sortfcn = revmodcmp; 234 } else { 235 if (!f_timesort) 236 sortfcn = namecmp; 237 else if (f_accesstime) 238 sortfcn = acccmp; 239 else if (f_statustime) 240 sortfcn = statcmp; 241 else /* Use modification time. */ 242 sortfcn = modcmp; 243 } 244 245 /* Select a print function. */ 246 if (f_singlecol) 247 printfcn = printscol; 248 else if (f_longform) 249 printfcn = printlong; 250 else 251 printfcn = printcol; 252 253 if (argc) 254 traverse(argc, argv, fts_options); 255 else 256 traverse(1, dotav, fts_options); 257 exit(0); 258 } 259 260 static int output; /* If anything output. */ 261 262 /* 263 * Traverse() walks the logical directory structure specified by the argv list 264 * in the order specified by the mastercmp() comparison function. During the 265 * traversal it passes linked lists of structures to display() which represent 266 * a superset (may be exact set) of the files to be displayed. 267 */ 268 static void 269 traverse(argc, argv, options) 270 int argc, options; 271 char *argv[]; 272 { 273 FTS *ftsp; 274 FTSENT *p, *chp; 275 int ch_options; 276 277 if ((ftsp = 278 fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 279 err(1, NULL); 280 281 display(NULL, fts_children(ftsp, 0)); 282 if (f_listdir) 283 return; 284 285 /* 286 * If not recursing down this tree and don't need stat info, just get 287 * the names. 288 */ 289 ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 290 291 while ((p = fts_read(ftsp)) != NULL) 292 switch (p->fts_info) { 293 case FTS_DC: 294 warnx("%s: directory causes a cycle", p->fts_name); 295 break; 296 case FTS_DNR: 297 case FTS_ERR: 298 warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); 299 break; 300 case FTS_D: 301 if (p->fts_level != FTS_ROOTLEVEL && 302 p->fts_name[0] == '.' && !f_listdot) 303 break; 304 305 /* 306 * If already output something, put out a newline as 307 * a separator. If multiple arguments, precede each 308 * directory with its name. 309 */ 310 if (output) 311 (void)printf("\n%s:\n", p->fts_path); 312 else if (argc > 1) { 313 (void)printf("%s:\n", p->fts_path); 314 output = 1; 315 } 316 317 chp = fts_children(ftsp, ch_options); 318 display(p, chp); 319 320 if (!f_recursive && chp != NULL) 321 (void)fts_set(ftsp, p, FTS_SKIP); 322 break; 323 } 324 if (errno) 325 err(1, "fts_read"); 326 } 327 328 /* 329 * Display() takes a linked list of FTSENT structures and passes the list 330 * along with any other necessary information to the print function. P 331 * points to the parent directory of the display list. 332 */ 333 static void 334 display(p, list) 335 FTSENT *p, *list; 336 { 337 struct stat *sp; 338 DISPLAY d; 339 FTSENT *cur; 340 NAMES *np; 341 u_quad_t maxsize; 342 u_long btotal, maxblock, maxinode, maxlen, maxnlink; 343 int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser; 344 int entries, needstats; 345 char *user, *group, *flags, buf[20]; /* 32 bits == 10 digits */ 346 347 /* 348 * If list is NULL there are two possibilities: that the parent 349 * directory p has no children, or that fts_children() returned an 350 * error. We ignore the error case since it will be replicated 351 * on the next call to fts_read() on the post-order visit to the 352 * directory p, and will be signalled in traverse(). 353 */ 354 if (list == NULL) 355 return; 356 357 needstats = f_inode || f_longform || f_size; 358 flen = 0; 359 btotal = maxblock = maxinode = maxlen = maxnlink = 0; 360 bcfile = 0; 361 maxuser = maxgroup = maxflags = 0; 362 maxsize = 0; 363 for (cur = list, entries = 0; cur; cur = cur->fts_link) { 364 if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 365 warnx("%s: %s", 366 cur->fts_name, strerror(cur->fts_errno)); 367 cur->fts_number = NO_PRINT; 368 continue; 369 } 370 371 /* 372 * P is NULL if list is the argv list, to which different rules 373 * apply. 374 */ 375 if (p == NULL) { 376 /* Directories will be displayed later. */ 377 if (cur->fts_info == FTS_D && !f_listdir) { 378 cur->fts_number = NO_PRINT; 379 continue; 380 } 381 } else { 382 /* Only display dot file if -a/-A set. */ 383 if (cur->fts_name[0] == '.' && !f_listdot) { 384 cur->fts_number = NO_PRINT; 385 continue; 386 } 387 } 388 if (f_nonprint) 389 prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); 390 if (cur->fts_namelen > maxlen) 391 maxlen = cur->fts_namelen; 392 if (needstats) { 393 sp = cur->fts_statp; 394 if (sp->st_blocks > maxblock) 395 maxblock = sp->st_blocks; 396 if (sp->st_ino > maxinode) 397 maxinode = sp->st_ino; 398 if (sp->st_nlink > maxnlink) 399 maxnlink = sp->st_nlink; 400 if (sp->st_size > maxsize) 401 maxsize = sp->st_size; 402 403 btotal += sp->st_blocks; 404 if (f_longform) { 405 user = user_from_uid(sp->st_uid, 0); 406 if ((ulen = strlen(user)) > maxuser) 407 maxuser = ulen; 408 group = group_from_gid(sp->st_gid, 0); 409 if ((glen = strlen(group)) > maxgroup) 410 maxgroup = glen; 411 if (f_flags) { 412 flags = 413 flags_to_string(sp->st_flags, "-"); 414 if ((flen = strlen(flags)) > maxflags) 415 maxflags = flen; 416 } else 417 flen = 0; 418 419 if ((np = malloc(sizeof(NAMES) + 420 ulen + glen + flen + 3)) == NULL) 421 err(1, NULL); 422 423 np->user = &np->data[0]; 424 (void)strcpy(np->user, user); 425 np->group = &np->data[ulen + 1]; 426 (void)strcpy(np->group, group); 427 428 if (S_ISCHR(sp->st_mode) || 429 S_ISBLK(sp->st_mode)) 430 bcfile = 1; 431 432 if (f_flags) { 433 np->flags = &np->data[ulen + glen + 2]; 434 (void)strcpy(np->flags, flags); 435 } 436 cur->fts_pointer = np; 437 } 438 } 439 ++entries; 440 } 441 442 if (!entries) 443 return; 444 445 d.list = list; 446 d.entries = entries; 447 d.maxlen = maxlen; 448 if (needstats) { 449 d.bcfile = bcfile; 450 d.btotal = btotal; 451 (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 452 d.s_block = strlen(buf); 453 d.s_flags = maxflags; 454 d.s_group = maxgroup; 455 (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 456 d.s_inode = strlen(buf); 457 (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 458 d.s_nlink = strlen(buf); 459 (void)snprintf(buf, sizeof(buf), "%qu", maxsize); 460 d.s_size = strlen(buf); 461 d.s_user = maxuser; 462 } 463 464 printfcn(&d); 465 output = 1; 466 467 if (f_longform) 468 for (cur = list; cur; cur = cur->fts_link) 469 free(cur->fts_pointer); 470 } 471 472 /* 473 * Ordering for mastercmp: 474 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 475 * as larger than directories. Within either group, use the sort function. 476 * All other levels use the sort function. Error entries remain unsorted. 477 */ 478 static int 479 mastercmp(a, b) 480 const FTSENT **a, **b; 481 { 482 int a_info, b_info; 483 484 a_info = (*a)->fts_info; 485 if (a_info == FTS_ERR) 486 return (0); 487 b_info = (*b)->fts_info; 488 if (b_info == FTS_ERR) 489 return (0); 490 491 if (a_info == FTS_NS || b_info == FTS_NS) 492 return (namecmp(*a, *b)); 493 494 if (a_info == b_info) 495 return (sortfcn(*a, *b)); 496 497 if ((*a)->fts_level == FTS_ROOTLEVEL) 498 if (a_info == FTS_D) 499 return (1); 500 else if (b_info == FTS_D) 501 return (-1); 502 else 503 return (sortfcn(*a, *b)); 504 else 505 return (sortfcn(*a, *b)); 506 } 507