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