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 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 char copyright[] = 13 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)ls.c 5.61 (Berkeley) 03/02/92"; 19 #endif /* not lint */ 20 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <sys/ioctl.h> 24 #include <dirent.h> 25 #include <unistd.h> 26 #include <fts.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <errno.h> 30 #include <stdio.h> 31 #include "ls.h" 32 #include "extern.h" 33 34 void display __P((FTSENT *, FTSENT *)); 35 char *getbsize __P((char *, int *, int *)); 36 int mastercmp __P((const FTSENT **, const FTSENT **)); 37 void traverse __P((int, char **, int)); 38 39 static void (*printfcn) __P((FTSENT *, int, u_long, int)); 40 static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); 41 42 int termwidth = 80; /* default terminal width */ 43 int blocksize; /* block size units */ 44 45 /* flags */ 46 int f_accesstime; /* use time of last access */ 47 int f_column; /* columnated format */ 48 int f_group; /* show group ownership of a file */ 49 int f_flags; /* show flags associated with a file */ 50 int f_inode; /* print inode */ 51 int f_listdir; /* list actual directory, not contents */ 52 int f_listdot; /* list files beginning with . */ 53 int f_longform; /* long listing format */ 54 int f_newline; /* if precede with newline */ 55 int f_nonprint; /* show unprintables as ? */ 56 int f_nosort; /* don't sort output */ 57 int f_recursive; /* ls subdirectories also */ 58 int f_reversesort; /* reverse whatever sort is used */ 59 int f_sectime; /* print the real time for all files */ 60 int f_singlecol; /* use single column output */ 61 int f_size; /* list size in short listing */ 62 int f_statustime; /* use time of last mode change */ 63 int f_dirname; /* if precede with directory name */ 64 int f_timesort; /* sort by time vice name */ 65 int f_type; /* add type character for non-regular files */ 66 67 int 68 main(argc, argv) 69 int argc; 70 char *argv[]; 71 { 72 static char dot[] = ".", *dotav[] = { dot, NULL }; 73 struct winsize win; 74 int ch, fts_options, notused; 75 char *p; 76 77 /* Terminal defaults to -Cq, non-terminal defaults to -1. */ 78 if (isatty(STDOUT_FILENO)) { 79 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || 80 !win.ws_col) { 81 if (p = getenv("COLUMNS")) 82 termwidth = atoi(p); 83 } 84 else 85 termwidth = win.ws_col; 86 f_column = f_nonprint = 1; 87 } else 88 f_singlecol = 1; 89 90 /* Root is -A automatically. */ 91 if (!getuid()) 92 f_listdot = 1; 93 94 fts_options = FTS_PHYSICAL; 95 while ((ch = getopt(argc, argv, "1ACFLRTacdfgikloqrstu")) != EOF) { 96 switch (ch) { 97 /* 98 * The -1, -C and -l options all override each other so shell 99 * aliasing works right. 100 */ 101 case '1': 102 f_singlecol = 1; 103 f_column = f_longform = 0; 104 break; 105 case 'C': 106 f_column = 1; 107 f_longform = f_singlecol = 0; 108 break; 109 case 'l': 110 f_longform = 1; 111 f_column = f_singlecol = 0; 112 break; 113 /* The -c and -u options override each other. */ 114 case 'c': 115 f_statustime = 1; 116 f_accesstime = 0; 117 break; 118 case 'u': 119 f_accesstime = 1; 120 f_statustime = 0; 121 break; 122 case 'F': 123 f_type = 1; 124 break; 125 case 'L': 126 fts_options &= ~FTS_PHYSICAL; 127 fts_options |= FTS_LOGICAL; 128 break; 129 case 'R': 130 f_recursive = 1; 131 break; 132 case 'a': 133 fts_options |= FTS_SEEDOT; 134 /* FALLTHROUGH */ 135 case 'A': 136 f_listdot = 1; 137 break; 138 /* The -d option turns off the -R option. */ 139 case 'd': 140 f_listdir = 1; 141 f_recursive = 0; 142 break; 143 case 'f': 144 f_nosort = 1; 145 break; 146 case 'g': 147 f_group = 1; 148 break; 149 case 'i': 150 f_inode = 1; 151 break; 152 case 'k': /* Delete before 4.4BSD. */ 153 (void)fprintf(stderr, "ls: -k no longer supported\n"); 154 break; 155 case 'o': 156 f_flags = 1; 157 break; 158 case 'q': 159 f_nonprint = 1; 160 break; 161 case 'r': 162 f_reversesort = 1; 163 break; 164 case 's': 165 f_size = 1; 166 break; 167 case 'T': 168 f_sectime = 1; 169 break; 170 case 't': 171 f_timesort = 1; 172 break; 173 default: 174 case '?': 175 usage(); 176 } 177 } 178 argc -= optind; 179 argv += optind; 180 181 /* 182 * If not -F, -i, -l, -s or -t options, don't require stat 183 * information. 184 */ 185 if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) 186 fts_options |= FTS_NOSTAT; 187 188 /* 189 * If not -F, -d or -l options, follow any symbolic links listed on 190 * the command line. 191 */ 192 if (!f_longform && !f_listdir && !f_type) 193 fts_options |= FTS_COMFOLLOW; 194 195 /* If -l or -s, figure out block size. */ 196 if (f_longform || f_size) { 197 (void)getbsize("ls", ¬used, &blocksize); 198 blocksize /= 512; 199 } 200 201 /* Select a sort function. */ 202 if (f_reversesort) { 203 if (!f_timesort) 204 sortfcn = revnamecmp; 205 else if (f_accesstime) 206 sortfcn = revacccmp; 207 else if (f_statustime) 208 sortfcn = revstatcmp; 209 else /* Use modification time. */ 210 sortfcn = revmodcmp; 211 } else { 212 if (!f_timesort) 213 sortfcn = namecmp; 214 else if (f_accesstime) 215 sortfcn = acccmp; 216 else if (f_statustime) 217 sortfcn = statcmp; 218 else /* Use modification time. */ 219 sortfcn = modcmp; 220 } 221 222 /* Select a print function. */ 223 if (f_singlecol) 224 printfcn = printscol; 225 else if (f_longform) 226 printfcn = printlong; 227 else 228 printfcn = printcol; 229 230 if (argc) 231 traverse(argc, argv, fts_options); 232 else 233 traverse(1, dotav, fts_options); 234 exit(0); 235 } 236 237 static int output; /* If anything output. */ 238 239 /* 240 * Traverse() walks the logical directory structure specified by the argv list 241 * in the order specified by the mastercmp() comparison function. During the 242 * traversal it passes linked lists of structures to display() which represent 243 * a superset (may be exact set) of the files to be displayed. 244 */ 245 void 246 traverse(argc, argv, options) 247 int argc, options; 248 char *argv[]; 249 { 250 register FTS *ftsp; 251 register FTSENT *p; 252 int ch_options; 253 254 if ((ftsp = 255 fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) 256 err(1, "fts_open: %s", strerror(errno)); 257 258 display(NULL, fts_children(ftsp, 0)); 259 if (f_listdir) 260 return; 261 262 /* 263 * If not recursing down this tree and don't need stat info, just get 264 * the names. 265 */ 266 ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; 267 268 while (p = fts_read(ftsp)) 269 switch(p->fts_info) { 270 case FTS_DC: 271 err(0, "%s: directory causes a cycle", p->fts_name); 272 break; 273 case FTS_DNR: 274 case FTS_ERR: 275 err(0, "%s: %s", 276 p->fts_name, strerror(p->fts_errno)); 277 break; 278 case FTS_D: 279 if (p->fts_level != FTS_ROOTLEVEL && 280 p->fts_name[0] == '.' && !f_listdot) 281 break; 282 283 /* 284 * If already output something, put out a newline as 285 * a separator. If multiple arguments, precede each 286 * directory with its name. 287 */ 288 if (output) 289 (void)printf("\n%s:\n", p->fts_path); 290 else if (argc > 1) { 291 (void)printf("%s:\n", p->fts_path); 292 output = 1; 293 } 294 295 display(p, fts_children(ftsp, ch_options)); 296 297 if (!f_recursive) 298 (void)fts_set(ftsp, p, FTS_SKIP); 299 break; 300 } 301 (void)fts_close(ftsp); 302 } 303 304 /* 305 * Display() takes a linked list of FTSENT structures passes the list along 306 * with any other necessary information to the print function (printfcn()). 307 * P points to the parent directory of the display list. 308 */ 309 void 310 display(p, list) 311 register FTSENT *p; 312 FTSENT *list; 313 { 314 register FTSENT *cur; 315 u_long btotal; 316 int entries, maxlen; 317 318 /* 319 * If list is NULL there are two possibilities: that the parent 320 * directory p has no children, or that fts_children() returned an 321 * error. We ignore the error case since it will be replicated 322 * on the next call to fts_read() on the post-order visit to the 323 * directory p, and will be signalled in traverse(). 324 */ 325 if (list == NULL) 326 return; 327 328 btotal = 0; 329 maxlen = 0; 330 for (cur = list, entries = 0; cur; cur = cur->fts_link) { 331 if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { 332 err(0, "%s: %s", 333 cur->fts_name, strerror(cur->fts_errno)); 334 cur->fts_number = NO_PRINT; 335 continue; 336 } 337 338 /* 339 * P is NULL if list is the argv list. Different rules apply 340 * to this list. 341 */ 342 if (p == NULL) { 343 /* Directories will be displayed later. */ 344 if (cur->fts_info == FTS_D && !f_listdir) { 345 cur->fts_number = NO_PRINT; 346 continue; 347 } 348 } else { 349 /* Only display dot file if -a/-A set. */ 350 if (cur->fts_name[0] == '.' && !f_listdot) { 351 cur->fts_number = NO_PRINT; 352 continue; 353 } 354 355 if (f_longform || f_size) 356 btotal += cur->fts_statp->st_blocks; 357 } 358 if (f_nonprint) 359 prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); 360 if (f_column && cur->fts_namelen > maxlen) 361 maxlen = cur->fts_namelen; 362 ++entries; 363 } 364 365 if (entries) { 366 printfcn(list, entries, btotal, maxlen); 367 output = 1; 368 } 369 } 370 371 /* 372 * Ordering for mastercmp: 373 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories 374 * as larger than directories. Within either group, use the sort function. 375 * All other levels use the sort function. Error entries remain unsorted. 376 */ 377 int 378 mastercmp(a, b) 379 const FTSENT **a, **b; 380 { 381 register int a_info, b_info; 382 383 a_info = (*a)->fts_info; 384 if (a_info == FTS_ERR) 385 return (0); 386 b_info = (*b)->fts_info; 387 if (b_info == FTS_ERR) 388 return (0); 389 390 if (a_info == FTS_NS || b_info == FTS_NS) 391 return (namecmp(*a, *b)); 392 393 if (a_info == b_info) 394 return (sortfcn(*a, *b)); 395 396 if ((*a)->fts_level == FTS_ROOTLEVEL) 397 if (a_info == FTS_D) 398 return (1); 399 else if (b_info == FTS_D) 400 return (-1); 401 else 402 return (sortfcn(*a, *b)); 403 else 404 return (sortfcn(*a, *b)); 405 } 406