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.52 (Berkeley) 12/12/91"; 19 #endif /* not lint */ 20 21 #include <sys/param.h> 22 #include <sys/stat.h> 23 #include <sys/ioctl.h> 24 #include <dirent.h> 25 #include <unistd.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <errno.h> 29 #include <stdio.h> 30 #include "ls.h" 31 #include "extern.h" 32 33 static void displaydir __P((LS *, int)); 34 static void doargs __P((int, char **)); 35 static void subdir __P((LS *)); 36 static int tabdir __P((LS *, LS **, char **)); 37 38 int termwidth = 80; /* default terminal width */ 39 40 /* flags */ 41 int f_accesstime; /* use time of last access */ 42 int f_column; /* columnated format */ 43 int f_group; /* show group ownership of a file */ 44 int f_flags; /* show flags associated with a file */ 45 int f_ignorelink; /* indirect through symbolic link operands */ 46 int f_inode; /* print inode */ 47 int f_kblocks; /* print size in kilobytes */ 48 int f_listalldot; /* list . and .. as well */ 49 int f_listdir; /* list actual directory, not contents */ 50 int f_listdot; /* list files beginning with . */ 51 int f_longform; /* long listing format */ 52 int f_needstat; /* if need to stat files */ 53 int f_newline; /* if precede with newline */ 54 int f_nonprint; /* show unprintables as ? */ 55 int f_nosort; /* don't sort output */ 56 int f_recursive; /* ls subdirectories also */ 57 int f_reversesort; /* reverse whatever sort is used */ 58 int f_sectime; /* print the real time for all files */ 59 int f_singlecol; /* use single column output */ 60 int f_size; /* list size in short listing */ 61 int f_statustime; /* use time of last mode change */ 62 int f_dirname; /* if precede with directory name */ 63 int f_timesort; /* sort by time vice name */ 64 int f_total; /* if precede with "total" line */ 65 int f_type; /* add type character for non-regular files */ 66 67 void (*printfcn)(); 68 int (*statfcn)(), (*sortfcn)(); 69 70 main(argc, argv) 71 int argc; 72 char **argv; 73 { 74 struct winsize win; 75 int ch; 76 char *p; 77 78 /* terminal defaults to -Cq, non-terminal defaults to -1 */ 79 if (isatty(1)) { 80 f_nonprint = 1; 81 if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { 82 if (p = getenv("COLUMNS")) 83 termwidth = atoi(p); 84 } 85 else 86 termwidth = win.ws_col; 87 f_column = 1; 88 } else 89 f_singlecol = 1; 90 91 /* root is -A automatically */ 92 if (!getuid()) 93 f_listdot = 1; 94 95 while ((ch = getopt(argc, argv, "1ACFLRTacdfgikloqrstu")) != EOF) { 96 switch (ch) { 97 /* 98 * -1, -C and -l all override each other 99 * so shell 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 /* -c and -u 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 f_ignorelink = 1; 127 break; 128 case 'R': 129 f_recursive = 1; 130 break; 131 case 'a': 132 f_listalldot = 1; 133 /* FALLTHROUGH */ 134 case 'A': 135 f_listdot = 1; 136 break; 137 case 'd': 138 f_listdir = 1; 139 break; 140 case 'f': 141 f_nosort = 1; 142 break; 143 case 'g': 144 f_group = 1; 145 break; 146 case 'i': 147 f_inode = 1; 148 break; 149 case 'k': 150 f_kblocks = 1; 151 break; 152 case 'o': 153 f_flags = 1; 154 break; 155 case 'q': 156 f_nonprint = 1; 157 break; 158 case 'r': 159 f_reversesort = 1; 160 break; 161 case 's': 162 f_size = 1; 163 break; 164 case 'T': 165 f_sectime = 1; 166 break; 167 case 't': 168 f_timesort = 1; 169 break; 170 default: 171 case '?': 172 usage(); 173 } 174 } 175 argc -= optind; 176 argv += optind; 177 178 /* -d turns off -R */ 179 if (f_listdir) 180 f_recursive = 0; 181 182 /* if need to stat files */ 183 f_needstat = f_longform || f_recursive || f_timesort || 184 f_size || f_type; 185 186 /* select a sort function */ 187 if (f_reversesort) { 188 if (!f_timesort) 189 sortfcn = revnamecmp; 190 else if (f_accesstime) 191 sortfcn = revacccmp; 192 else if (f_statustime) 193 sortfcn = revstatcmp; 194 else /* use modification time */ 195 sortfcn = revmodcmp; 196 } else { 197 if (!f_timesort) 198 sortfcn = namecmp; 199 else if (f_accesstime) 200 sortfcn = acccmp; 201 else if (f_statustime) 202 sortfcn = statcmp; 203 else /* use modification time */ 204 sortfcn = modcmp; 205 } 206 207 /* select a print function */ 208 if (f_singlecol) 209 printfcn = printscol; 210 else if (f_longform) 211 printfcn = printlong; 212 else 213 printfcn = printcol; 214 215 /* if -l, -d or -F, and not ignoring the link, use lstat() */ 216 statfcn = 217 (f_longform || f_listdir || f_type) && !f_ignorelink ? lstat : stat; 218 219 if (argc) 220 doargs(argc, argv); 221 else { 222 static char dot[] = ".", *dotav[] = { dot, NULL }; 223 doargs(1, dotav); 224 } 225 exit(0); 226 } 227 228 static char path[MAXPATHLEN + 1]; 229 static char *endofpath = path; 230 231 static void 232 doargs(argc, argv) 233 int argc; 234 char **argv; 235 { 236 register LS *dstatp, *rstatp; 237 register int cnt, dircnt, maxlen, regcnt; 238 LS *dstats, *rstats; 239 struct stat sb; 240 char top[MAXPATHLEN + 1]; 241 u_long blocks; 242 243 /* 244 * walk through the operands, building separate arrays of LS 245 * structures for directory and non-directory files. 246 */ 247 dstats = rstats = NULL; 248 for (dircnt = regcnt = 0; *argv; ++argv) { 249 if (statfcn(*argv, &sb) && 250 (statfcn == lstat || lstat(*argv, &sb))) { 251 if (errno == ENOENT) { 252 warn("%s: %s", *argv, strerror(errno)); 253 continue; 254 } 255 err("%s: %s", *argv, strerror(errno)); 256 } 257 if (S_ISDIR(sb.st_mode) && !f_listdir) { 258 if (!dstats) 259 dstatp = dstats = 260 emalloc((u_int)argc * (sizeof(LS))); 261 dstatp->name = *argv; 262 dstatp->lstat = sb; 263 ++dstatp; 264 ++dircnt; 265 } 266 else { 267 if (!rstats) { 268 rstatp = rstats = 269 emalloc((u_int)argc * (sizeof(LS))); 270 blocks = 0; 271 maxlen = -1; 272 } 273 rstatp->name = *argv; 274 rstatp->lstat = sb; 275 276 /* save name length for -C format */ 277 rstatp->len = strlen(*argv); 278 279 if (f_nonprint) 280 prcopy(*argv, *argv, rstatp->len); 281 282 /* calculate number of blocks if -l/-s formats */ 283 if (f_longform || f_size) 284 blocks += sb.st_blocks; 285 286 /* save max length if -C format */ 287 if (f_column && maxlen < rstatp->len) 288 maxlen = rstatp->len; 289 290 ++rstatp; 291 ++regcnt; 292 } 293 } 294 /* display regular files */ 295 if (regcnt) { 296 rstats[0].lstat.st_btotal = blocks; 297 rstats[0].lstat.st_maxlen = maxlen; 298 displaydir(rstats, regcnt); 299 f_newline = f_dirname = 1; 300 } 301 /* display directories */ 302 if (dircnt) { 303 register char *p; 304 305 f_total = 1; 306 if (dircnt > 1) { 307 (void)getwd(top); 308 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn); 309 f_dirname = 1; 310 } 311 for (cnt = 0; cnt < dircnt; ++dstats) { 312 for (endofpath = path, p = dstats->name; 313 *endofpath = *p++; ++endofpath); 314 subdir(dstats); 315 f_newline = 1; 316 if (++cnt < dircnt && chdir(top)) 317 err("%s: %s", top, strerror(errno)); 318 } 319 } 320 } 321 322 static void 323 displaydir(stats, num) 324 LS *stats; 325 register int num; 326 { 327 register char *p, *savedpath; 328 LS *lp; 329 330 if (num > 1 && !f_nosort) { 331 u_long save1, save2; 332 333 save1 = stats[0].lstat.st_btotal; 334 save2 = stats[0].lstat.st_maxlen; 335 qsort((char *)stats, num, sizeof(LS), sortfcn); 336 stats[0].lstat.st_btotal = save1; 337 stats[0].lstat.st_maxlen = save2; 338 } 339 340 printfcn(stats, num); 341 342 if (f_recursive) { 343 savedpath = endofpath; 344 for (lp = stats; num--; ++lp) { 345 if (!S_ISDIR(lp->lstat.st_mode)) 346 continue; 347 p = lp->name; 348 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2])) 349 continue; 350 if (endofpath != path && endofpath[-1] != '/') 351 *endofpath++ = '/'; 352 for (; *endofpath = *p++; ++endofpath); 353 f_newline = f_dirname = f_total = 1; 354 subdir(lp); 355 *(endofpath = savedpath) = '\0'; 356 } 357 } 358 } 359 360 static void 361 subdir(lp) 362 LS *lp; 363 { 364 LS *stats; 365 int num; 366 char *names; 367 368 if (f_newline) 369 (void)putchar('\n'); 370 if (f_dirname) 371 (void)printf("%s:\n", path); 372 373 if (chdir(lp->name)) { 374 warn("%s: %s", lp->name, strerror(errno)); 375 return; 376 } 377 if (num = tabdir(lp, &stats, &names)) { 378 displaydir(stats, num); 379 free(stats); 380 free(names); 381 } 382 if (chdir("..")) 383 err("..: %s", strerror(errno)); 384 } 385 386 static int 387 tabdir(lp, s_stats, s_names) 388 LS *lp, **s_stats; 389 char **s_names; 390 { 391 register DIR *dirp; 392 register int cnt, maxentry, maxlen; 393 register char *p, *names; 394 struct dirent *dp; 395 LS *stats; 396 u_long blocks; 397 398 if (!(dirp = opendir("."))) { 399 warn("%s: %s", lp->name, strerror(errno)); 400 return (0); 401 } 402 blocks = maxentry = maxlen = 0; 403 stats = NULL; 404 for (cnt = 0; dp = readdir(dirp);) { 405 /* this does -A and -a */ 406 p = dp->d_name; 407 if (p[0] == '.') { 408 if (!f_listdot) 409 continue; 410 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2])) 411 continue; 412 } 413 if (cnt == maxentry) { 414 if (!maxentry) 415 *s_names = names = 416 emalloc((u_int)lp->lstat.st_size); 417 #define DEFNUM 256 418 maxentry += DEFNUM; 419 if (!(*s_stats = stats = realloc((char *)stats, 420 (u_int)maxentry * sizeof(LS)))) 421 err("%s", strerror(errno)); 422 } 423 if (f_needstat && statfcn(dp->d_name, &stats[cnt].lstat) && 424 statfcn == stat && lstat(dp->d_name, &stats[cnt].lstat)) { 425 /* 426 * don't exit -- this could be an NFS mount that has 427 * gone away. Flush stdout so the messages line up. 428 */ 429 (void)fflush(stdout); 430 warn("%s: %s", dp->d_name, strerror(errno)); 431 continue; 432 } 433 stats[cnt].name = names; 434 435 if (f_nonprint) 436 prcopy(dp->d_name, names, (int)dp->d_namlen); 437 else 438 bcopy(dp->d_name, names, (int)dp->d_namlen); 439 names += dp->d_namlen; 440 *names++ = '\0'; 441 442 /* 443 * get the inode from the directory, so the -f flag 444 * works right. 445 */ 446 stats[cnt].lstat.st_ino = dp->d_ino; 447 448 /* save name length for -C format */ 449 stats[cnt].len = dp->d_namlen; 450 451 /* calculate number of blocks if -l/-s formats */ 452 if (f_longform || f_size) 453 blocks += stats[cnt].lstat.st_blocks; 454 455 /* save max length if -C format */ 456 if (f_column && maxlen < (int)dp->d_namlen) 457 maxlen = dp->d_namlen; 458 ++cnt; 459 } 460 (void)closedir(dirp); 461 462 if (cnt) { 463 stats[0].lstat.st_btotal = blocks; 464 stats[0].lstat.st_maxlen = maxlen; 465 } else if (stats) { 466 free(stats); 467 free(names); 468 } 469 return (cnt); 470 } 471