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