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