ls.c revision 1.7
1/* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * 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 38static char copyright[] = 39"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 40 All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44/*static char sccsid[] = "from: @(#)ls.c 5.69 (Berkeley) 10/17/92";*/ 45static char rcsid[] = "$Id: ls.c,v 1.7 1993/08/07 03:56:58 mycroft Exp $"; 46#endif /* not lint */ 47 48#include <sys/types.h> 49#include <sys/stat.h> 50#include <sys/ioctl.h> 51#include <dirent.h> 52#include <unistd.h> 53#include <fts.h> 54#include <stdlib.h> 55#include <string.h> 56#include <errno.h> 57#include <stdio.h> 58#include "ls.h" 59#include "extern.h" 60 61char *getbsize __P((char *, int *, long *, int)); 62char *group_from_gid __P((u_int, int)); 63char *user_from_uid __P((u_int, int)); 64 65static void display __P((FTSENT *, FTSENT *)); 66static char *flags_from_fid __P((u_long)); 67static int mastercmp __P((const FTSENT **, const FTSENT **)); 68static void traverse __P((int, char **, int)); 69 70static void (*printfcn) __P((DISPLAY *)); 71static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); 72 73long blocksize; /* block size units */ 74int termwidth = 80; /* default terminal width */ 75 76/* flags */ 77int f_accesstime; /* use time of last access */ 78int f_column; /* columnated format */ 79int f_flags; /* show flags associated with a file */ 80int f_inode; /* print inode */ 81int f_listdir; /* list actual directory, not contents */ 82int f_listdot; /* list files beginning with . */ 83int f_longform; /* long listing format */ 84int f_newline; /* if precede with newline */ 85int f_nonprint; /* show unprintables as ? */ 86int f_nosort; /* don't sort output */ 87int f_recursive; /* ls subdirectories also */ 88int f_reversesort; /* reverse whatever sort is used */ 89int f_sectime; /* print the real time for all files */ 90int f_singlecol; /* use single column output */ 91int f_size; /* list size in short listing */ 92int f_statustime; /* use time of last mode change */ 93int f_dirname; /* if precede with directory name */ 94int f_timesort; /* sort by time vice name */ 95int f_type; /* add type character for non-regular files */ 96 97int 98main(argc, argv) 99 int argc; 100 char *argv[]; 101{ 102 static char dot[] = ".", *dotav[] = { dot, NULL }; 103 struct winsize win; 104 int ch, fts_options, notused; 105 int kflag = 0; 106 char *p; 107 108 /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 109 if (isatty(STDOUT_FILENO)) { 110 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || 111 !win.ws_col) { 112 if (p = getenv("COLUMNS")) 113 termwidth = atoi(p); 114 } 115 else 116 termwidth = win.ws_col; 117 f_column = f_nonprint = 1; 118 } else 119 f_singlecol = 1; 120 121 /* Root is -A automatically. */ 122 if (!getuid()) 123 f_listdot = 1; 124 125 fts_options = FTS_PHYSICAL; 126 while ((ch = getopt(argc, argv, "1ACFLRTacdfgikloqrstu")) != EOF) { 127 switch (ch) { 128 /* 129 * The -1, -C and -l options all override each other so shell 130 * aliasing works right. 131 */ 132 case '1': 133 f_singlecol = 1; 134 f_column = f_longform = 0; 135 break; 136 case 'C': 137 f_column = 1; 138 f_longform = f_singlecol = 0; 139 break; 140 case 'l': 141 f_longform = 1; 142 f_column = f_singlecol = 0; 143 break; 144 /* The -c and -u options override each other. */ 145 case 'c': 146 f_statustime = 1; 147 f_accesstime = 0; 148 break; 149 case 'u': 150 f_accesstime = 1; 151 f_statustime = 0; 152 break; 153 case 'F': 154 f_type = 1; 155 break; 156 case 'L': 157 fts_options &= ~FTS_PHYSICAL; 158 fts_options |= FTS_LOGICAL; 159 break; 160 case 'R': 161 f_recursive = 1; 162 break; 163 case 'a': 164 fts_options |= FTS_SEEDOT; 165 /* FALLTHROUGH */ 166 case 'A': 167 f_listdot = 1; 168 break; 169 /* The -d option turns off the -R option. */ 170 case 'd': 171 f_listdir = 1; 172 f_recursive = 0; 173 break; 174 case 'f': 175 f_nosort = 1; 176 break; 177 case 'g': /* Compatibility with 4.3BSD. */ 178 break; 179 case 'i': 180 f_inode = 1; 181 break; 182 case 'k': 183 blocksize = 1024; 184 kflag = 1; 185 break; 186 case 'o': 187 f_flags = 1; 188 break; 189 case 'q': 190 f_nonprint = 1; 191 break; 192 case 'r': 193 f_reversesort = 1; 194 break; 195 case 's': 196 f_size = 1; 197 break; 198 case 'T': 199 f_sectime = 1; 200 break; 201 case 't': 202 f_timesort = 1; 203 break; 204 default: 205 case '?': 206 usage(); 207 } 208 } 209 argc -= optind; 210 argv += optind; 211 212 /* 213 * If not -F, -i, -l, -s or -t options, don't require stat 214 * information. 215 */ 216 if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) 217 fts_options |= FTS_NOSTAT; 218 219 /* 220 * If not -F, -d or -l options, follow any symbolic links listed on 221 * the command line. 222 */ 223 if (!f_longform && !f_listdir && !f_type) 224 fts_options |= FTS_COMFOLLOW; 225 226 /* If -l or -s, figure out block size. */ 227 if (f_longform || f_size) { 228 (void)getbsize("ls", ¬used, &blocksize, kflag); 229 blocksize /= 512; 230 } 231 232 /* Select a sort function. */ 233 if (f_reversesort) { 234 if (!f_timesort) 235 sortfcn = revnamecmp; 236 else if (f_accesstime) 237 sortfcn = revacccmp; 238 else if (f_statustime) 239 sortfcn = revstatcmp; 240 else /* Use modification time. */ 241 sortfcn = revmodcmp; 242 } else { 243 if (!f_timesort) 244 sortfcn = namecmp; 245 else if (f_accesstime) 246 sortfcn = acccmp; 247 else if (f_statustime) 248 sortfcn = statcmp; 249 else /* Use modification time. */ 250 sortfcn = modcmp; 251 } 252 253 /* Select a print function. */ 254 if (f_singlecol) 255 printfcn = printscol; 256 else if (f_longform) 257 printfcn = printlong; 258 else 259 printfcn = printcol; 260 261 if (argc) 262 traverse(argc, argv, fts_options); 263 else 264 traverse(1, dotav, fts_options); 265 exit(0); 266} 267 268static int output; /* If anything output. */ 269 270/* 271 * Traverse() walks the logical directory structure specified by the argv list 272 * in the order specified by the mastercmp() comparison function. During the 273 * traversal it passes linked lists of structures to display() which represent 274 * a superset (may be exact set) of the files to be displayed. 275 */ 276static void 277traverse(argc, argv, options) 278 int argc, options; 279 char *argv[]; 280{ 281 register FTS *ftsp; 282 register FTSENT *p; 283 int ch_options; 284 285 if ((ftsp = 286 fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 287 err(1, "%s", strerror(errno)); 288 289 display(NULL, fts_children(ftsp, 0)); 290 if (f_listdir) 291 return; 292 293 /* 294 * If not recursing down this tree and don't need stat info, just get 295 * the names. 296 */ 297 ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 298 299 while (p = fts_read(ftsp)) 300 switch(p->fts_info) { 301 case FTS_DC: 302 err(0, "%s: directory causes a cycle", p->fts_name); 303 break; 304 case FTS_DNR: 305 case FTS_ERR: 306 err(0, "%s: %s", 307 p->fts_name, strerror(p->fts_errno)); 308 break; 309 case FTS_D: 310 if (p->fts_level != FTS_ROOTLEVEL && 311 p->fts_name[0] == '.' && !f_listdot) 312 break; 313 314 /* 315 * If already output something, put out a newline as 316 * a separator. If multiple arguments, precede each 317 * directory with its name. 318 */ 319 if (output) 320 (void)printf("\n%s:\n", p->fts_path); 321 else if (argc > 1) { 322 (void)printf("%s:\n", p->fts_path); 323 output = 1; 324 } 325 326 display(p, fts_children(ftsp, ch_options)); 327 328 if (!f_recursive) 329 (void)fts_set(ftsp, p, FTS_SKIP); 330 break; 331 } 332 (void)fts_close(ftsp); 333} 334 335/* 336 * Display() takes a linked list of FTSENT structures and passes the list 337 * along with any other necessary information to the print function. P 338 * points to the parent directory of the display list. 339 */ 340static void 341display(p, list) 342 register FTSENT *p; 343 FTSENT *list; 344{ 345 register FTSENT *cur; 346 struct stat *sp; 347 DISPLAY d; 348 NAMES *np; 349 u_long btotal, maxblock, maxinode, maxlen, maxnlink; 350#ifdef notyet 351 u_quad_t maxsize; 352#else 353 off_t maxsize; 354#endif 355 int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser; 356 int entries, needstats; 357 char *user, *group, *flags, buf[20]; /* 32 bits == 10 digits */ 358 359 /* 360 * If list is NULL there are two possibilities: that the parent 361 * directory p has no children, or that fts_children() returned an 362 * error. We ignore the error case since it will be replicated 363 * on the next call to fts_read() on the post-order visit to the 364 * directory p, and will be signalled in traverse(). 365 */ 366 if (list == NULL) 367 return; 368 369 needstats = f_inode || f_longform || f_size; 370 flen = 0; 371 btotal = maxblock = maxinode = maxlen = maxnlink = 0; 372 bcfile = 0; 373 maxuser = maxgroup = maxflags = 0; 374 maxsize = 0; 375 for (cur = list, entries = 0; cur; cur = cur->fts_link) { 376 if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 377 err(0, "%s: %s", 378 cur->fts_name, strerror(cur->fts_errno)); 379 cur->fts_number = NO_PRINT; 380 continue; 381 } 382 383 /* 384 * P is NULL if list is the argv list, to which different rules 385 * apply. 386 */ 387 if (p == NULL) { 388 /* Directories will be displayed later. */ 389 if (cur->fts_info == FTS_D && !f_listdir) { 390 cur->fts_number = NO_PRINT; 391 continue; 392 } 393 } else { 394 /* Only display dot file if -a/-A set. */ 395 if (cur->fts_name[0] == '.' && !f_listdot) { 396 cur->fts_number = NO_PRINT; 397 continue; 398 } 399 } 400 if (f_nonprint) 401 prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); 402 if (cur->fts_namelen > maxlen) 403 maxlen = cur->fts_namelen; 404 if (needstats) { 405 sp = cur->fts_statp; 406 if (sp->st_blocks > maxblock) 407 maxblock = sp->st_blocks; 408 if (sp->st_ino > maxinode) 409 maxinode = sp->st_ino; 410 if (sp->st_nlink > maxnlink) 411 maxnlink = sp->st_nlink; 412 if (sp->st_size > maxsize) 413 maxsize = sp->st_size; 414 415 btotal += sp->st_blocks; 416 if (f_longform) { 417 user = user_from_uid(sp->st_uid, 0); 418 if ((ulen = strlen(user)) > maxuser) 419 maxuser = ulen; 420 group = group_from_gid(sp->st_gid, 0); 421 if ((glen = strlen(group)) > maxgroup) 422 maxgroup = glen; 423 if (f_flags) { 424 flags = flags_from_fid(sp->st_flags); 425 if ((flen = strlen(flags)) > maxflags) 426 maxflags = flen; 427 } else 428 flen = 0; 429 430 if ((np = malloc(sizeof(NAMES) + 431 ulen + glen + flen + 3)) == NULL) 432 err(1, "%s", strerror(errno)); 433 434 np->user = &np->data[0]; 435 (void)strcpy(np->user, user); 436 np->group = &np->data[ulen + 1]; 437 (void)strcpy(np->group, group); 438 439 if (S_ISCHR(sp->st_mode) || 440 S_ISBLK(sp->st_mode)) 441 bcfile = 1; 442 443 if (f_flags) { 444 np->flags = &np->data[ulen + glen + 2]; 445 (void)strcpy(np->flags, flags); 446 } 447 cur->fts_pointer = np; 448 } 449 } 450 ++entries; 451 } 452 453 if (!entries) 454 return; 455 456 d.list = list; 457 d.entries = entries; 458 d.maxlen = maxlen; 459 if (needstats) { 460 d.bcfile = bcfile; 461 d.btotal = btotal; 462 (void)snprintf(buf, sizeof(buf), "%lu", maxblock); 463 d.s_block = strlen(buf); 464 d.s_flags = maxflags; 465 d.s_group = maxgroup; 466 (void)snprintf(buf, sizeof(buf), "%lu", maxinode); 467 d.s_inode = strlen(buf); 468 (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); 469 d.s_nlink = strlen(buf); 470#ifdef notyet 471 (void)snprintf(buf, sizeof(buf), "%qu", maxsize); 472#else 473 (void)snprintf(buf, sizeof(buf), "%lu", maxsize); 474#endif 475 d.s_size = strlen(buf); 476 d.s_user = maxuser; 477 } 478 479 printfcn(&d); 480 output = 1; 481 482 if (f_longform) 483 for (cur = list; cur; cur = cur->fts_link) 484 free(cur->fts_pointer); 485} 486 487/* 488 * Ordering for mastercmp: 489 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 490 * as larger than directories. Within either group, use the sort function. 491 * All other levels use the sort function. Error entries remain unsorted. 492 */ 493static int 494mastercmp(a, b) 495 const FTSENT **a, **b; 496{ 497 register int a_info, b_info; 498 499 a_info = (*a)->fts_info; 500 if (a_info == FTS_ERR) 501 return (0); 502 b_info = (*b)->fts_info; 503 if (b_info == FTS_ERR) 504 return (0); 505 506 if (a_info == FTS_NS || b_info == FTS_NS) 507 return (namecmp(*a, *b)); 508 509 if (a_info == b_info) 510 return (sortfcn(*a, *b)); 511 512 if ((*a)->fts_level == FTS_ROOTLEVEL) 513 if (a_info == FTS_D) 514 return (1); 515 else if (b_info == FTS_D) 516 return (-1); 517 else 518 return (sortfcn(*a, *b)); 519 else 520 return (sortfcn(*a, *b)); 521} 522 523static char * 524flags_from_fid(flags) 525 u_long flags; 526{ 527 static char buf[20]; 528 register int comma; 529 register char *p; 530 531 p = buf; 532#ifdef notyet 533 if (flags & ARCHIVED) { 534 (void)strcpy(p, "arch"); 535 p += sizeof("arch") - 1; 536 comma = 1; 537 } else 538 comma = 0; 539 if (flags & NODUMP) { 540 if (comma++) 541 *p++ = ','; 542 (void)strcpy(p, "nodump"); 543 p += sizeof("nodump") - 1; 544 } 545 if (flags & IMMUTABLE) { 546 if (comma++) 547 *p++ = ','; 548 (void)strcpy(p, "nochg"); 549 p += sizeof("nochg") - 1; 550 } 551 if (!comma) 552#endif 553 (void)strcpy(p, "-"); 554 return (buf); 555} 556