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