1 /* 2 * Copyright (c) 1999 - 2000 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of KTH nor the names of its contributors may be 18 * used to endorse or promote products derived from this software without 19 * specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 32 33 #ifndef TEST 34 #include "ftpd_locl.h" 35 36 RCSID("$Id: ls.c,v 1.20 2001/01/25 01:33:15 joda Exp $"); 37 38 #else 39 #include <stdio.h> 40 #include <string.h> 41 #include <stdlib.h> 42 #include <time.h> 43 #include <dirent.h> 44 #include <sys/stat.h> 45 #include <unistd.h> 46 #include <pwd.h> 47 #include <grp.h> 48 #include <errno.h> 49 50 #define sec_fprintf2 fprintf 51 #define sec_fflush fflush 52 void 53 builtin_ls(FILE *out, const char *file); 54 int 55 main(int argc, char **argv) 56 { 57 int i; 58 for(i = 1; i < argc; i++) 59 builtin_ls(stdout, argv[i]); 60 return 0; 61 } 62 #endif 63 64 struct fileinfo { 65 struct stat st; 66 int inode; 67 int bsize; 68 char mode[11]; 69 int n_link; 70 char *user; 71 char *group; 72 char *size; 73 char *major; 74 char *minor; 75 char *date; 76 char *filename; 77 char *link; 78 }; 79 80 static void 81 free_fileinfo(struct fileinfo *f) 82 { 83 free(f->user); 84 free(f->group); 85 free(f->size); 86 free(f->major); 87 free(f->minor); 88 free(f->date); 89 free(f->filename); 90 free(f->link); 91 } 92 93 #define LS_DIRS (1 << 0) 94 #define LS_IGNORE_DOT (1 << 1) 95 #define LS_SORT_MODE (3 << 2) 96 #define SORT_MODE(f) ((f) & LS_SORT_MODE) 97 #define LS_SORT_NAME (1 << 2) 98 #define LS_SORT_MTIME (2 << 2) 99 #define LS_SORT_SIZE (3 << 2) 100 #define LS_SORT_REVERSE (1 << 4) 101 102 #define LS_SIZE (1 << 5) 103 #define LS_INODE (1 << 6) 104 #define LS_TYPE (1 << 7) 105 #define LS_DISP_MODE (3 << 8) 106 #define DISP_MODE(f) ((f) & LS_DISP_MODE) 107 #define LS_DISP_LONG (1 << 8) 108 #define LS_DISP_COLUMN (2 << 8) 109 #define LS_DISP_CROSS (3 << 8) 110 111 #ifndef S_ISTXT 112 #define S_ISTXT S_ISVTX 113 #endif 114 115 #ifndef S_ISSOCK 116 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK) 117 #endif 118 119 #ifndef S_ISLNK 120 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK) 121 #endif 122 123 static void 124 make_fileinfo(const char *filename, struct fileinfo *file, int flags) 125 { 126 char buf[128]; 127 int file_type = 0; 128 struct stat *st = &file->st; 129 130 file->inode = st->st_ino; 131 #ifdef S_BLKSIZE 132 file->bsize = st->st_blocks * S_BLKSIZE / 1024; 133 #else 134 file->bsize = st->st_blocks * 512 / 1024; 135 #endif 136 137 if(S_ISDIR(st->st_mode)) { 138 file->mode[0] = 'd'; 139 file_type = '/'; 140 } 141 else if(S_ISCHR(st->st_mode)) 142 file->mode[0] = 'c'; 143 else if(S_ISBLK(st->st_mode)) 144 file->mode[0] = 'b'; 145 else if(S_ISREG(st->st_mode)) { 146 file->mode[0] = '-'; 147 if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 148 file_type = '*'; 149 } 150 else if(S_ISFIFO(st->st_mode)) { 151 file->mode[0] = 'p'; 152 file_type = '|'; 153 } 154 else if(S_ISLNK(st->st_mode)) { 155 file->mode[0] = 'l'; 156 file_type = '@'; 157 } 158 else if(S_ISSOCK(st->st_mode)) { 159 file->mode[0] = 's'; 160 file_type = '='; 161 } 162 #ifdef S_ISWHT 163 else if(S_ISWHT(st->st_mode)) { 164 file->mode[0] = 'w'; 165 file_type = '%'; 166 } 167 #endif 168 else 169 file->mode[0] = '?'; 170 { 171 char *x[] = { "---", "--x", "-w-", "-wx", 172 "r--", "r-x", "rw-", "rwx" }; 173 strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]); 174 strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]); 175 strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]); 176 if((st->st_mode & S_ISUID)) { 177 if((st->st_mode & S_IXUSR)) 178 file->mode[3] = 's'; 179 else 180 file->mode[3] = 'S'; 181 } 182 if((st->st_mode & S_ISGID)) { 183 if((st->st_mode & S_IXGRP)) 184 file->mode[6] = 's'; 185 else 186 file->mode[6] = 'S'; 187 } 188 if((st->st_mode & S_ISTXT)) { 189 if((st->st_mode & S_IXOTH)) 190 file->mode[9] = 't'; 191 else 192 file->mode[9] = 'T'; 193 } 194 } 195 file->n_link = st->st_nlink; 196 { 197 struct passwd *pwd; 198 pwd = getpwuid(st->st_uid); 199 if(pwd == NULL) 200 asprintf(&file->user, "%u", (unsigned)st->st_uid); 201 else 202 file->user = strdup(pwd->pw_name); 203 } 204 { 205 struct group *grp; 206 grp = getgrgid(st->st_gid); 207 if(grp == NULL) 208 asprintf(&file->group, "%u", (unsigned)st->st_gid); 209 else 210 file->group = strdup(grp->gr_name); 211 } 212 213 if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 214 #if defined(major) && defined(minor) 215 asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)); 216 asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)); 217 #else 218 /* Don't want to use the DDI/DKI crap. */ 219 asprintf(&file->major, "%u", (unsigned)st->st_rdev); 220 asprintf(&file->minor, "%u", 0); 221 #endif 222 } else 223 asprintf(&file->size, "%lu", (unsigned long)st->st_size); 224 225 { 226 time_t t = time(NULL); 227 time_t mtime = st->st_mtime; 228 struct tm *tm = localtime(&mtime); 229 if((t - mtime > 6*30*24*60*60) || 230 (mtime - t > 6*30*24*60*60)) 231 strftime(buf, sizeof(buf), "%b %e %Y", tm); 232 else 233 strftime(buf, sizeof(buf), "%b %e %H:%M", tm); 234 file->date = strdup(buf); 235 } 236 { 237 const char *p = strrchr(filename, '/'); 238 if(p) 239 p++; 240 else 241 p = filename; 242 if((flags & LS_TYPE) && file_type != 0) 243 asprintf(&file->filename, "%s%c", p, file_type); 244 else 245 file->filename = strdup(p); 246 } 247 if(S_ISLNK(st->st_mode)) { 248 int n; 249 n = readlink((char *)filename, buf, sizeof(buf)); 250 if(n >= 0) { 251 buf[n] = '\0'; 252 file->link = strdup(buf); 253 } else 254 warn("%s: readlink", filename); 255 } 256 } 257 258 static void 259 print_file(FILE *out, 260 int flags, 261 struct fileinfo *f, 262 int max_inode, 263 int max_bsize, 264 int max_n_link, 265 int max_user, 266 int max_group, 267 int max_size, 268 int max_major, 269 int max_minor, 270 int max_date) 271 { 272 if(f->filename == NULL) 273 return; 274 275 if(flags & LS_INODE) { 276 sec_fprintf2(out, "%*d", max_inode, f->inode); 277 sec_fprintf2(out, " "); 278 } 279 if(flags & LS_SIZE) { 280 sec_fprintf2(out, "%*d", max_bsize, f->bsize); 281 sec_fprintf2(out, " "); 282 } 283 sec_fprintf2(out, "%s", f->mode); 284 sec_fprintf2(out, " "); 285 sec_fprintf2(out, "%*d", max_n_link, f->n_link); 286 sec_fprintf2(out, " "); 287 sec_fprintf2(out, "%-*s", max_user, f->user); 288 sec_fprintf2(out, " "); 289 sec_fprintf2(out, "%-*s", max_group, f->group); 290 sec_fprintf2(out, " "); 291 if(f->major != NULL && f->minor != NULL) 292 sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor); 293 else 294 sec_fprintf2(out, "%*s", max_size, f->size); 295 sec_fprintf2(out, " "); 296 sec_fprintf2(out, "%*s", max_date, f->date); 297 sec_fprintf2(out, " "); 298 sec_fprintf2(out, "%s", f->filename); 299 if(f->link) 300 sec_fprintf2(out, " -> %s", f->link); 301 sec_fprintf2(out, "\r\n"); 302 } 303 304 static int 305 compare_filename(struct fileinfo *a, struct fileinfo *b) 306 { 307 if(a->filename == NULL) 308 return 1; 309 if(b->filename == NULL) 310 return -1; 311 return strcmp(a->filename, b->filename); 312 } 313 314 static int 315 compare_mtime(struct fileinfo *a, struct fileinfo *b) 316 { 317 if(a->filename == NULL) 318 return 1; 319 if(b->filename == NULL) 320 return -1; 321 return b->st.st_mtime - a->st.st_mtime; 322 } 323 324 static int 325 compare_size(struct fileinfo *a, struct fileinfo *b) 326 { 327 if(a->filename == NULL) 328 return 1; 329 if(b->filename == NULL) 330 return -1; 331 return b->st.st_size - a->st.st_size; 332 } 333 334 static void 335 list_dir(FILE *out, const char *directory, int flags); 336 337 static int 338 log10(int num) 339 { 340 int i = 1; 341 while(num > 10) { 342 i++; 343 num /= 10; 344 } 345 return i; 346 } 347 348 /* 349 * Operate as lstat but fake up entries for AFS mount points so we don't 350 * have to fetch them. 351 */ 352 353 #ifdef KRB4 354 static int do_the_afs_dance = 1; 355 #endif 356 357 static int 358 lstat_file (const char *file, struct stat *sb) 359 { 360 #ifdef KRB4 361 if (do_the_afs_dance && 362 k_hasafs() 363 && strcmp(file, ".") 364 && strcmp(file, "..") 365 && strcmp(file, "/")) 366 { 367 struct ViceIoctl a_params; 368 char *dir, *last; 369 char *path_bkp; 370 static ino_t ino_counter = 0, ino_last = 0; 371 int ret; 372 const int maxsize = 2048; 373 374 path_bkp = strdup (file); 375 if (path_bkp == NULL) 376 return -1; 377 378 a_params.out = malloc (maxsize); 379 if (a_params.out == NULL) { 380 free (path_bkp); 381 return -1; 382 } 383 384 /* If path contains more than the filename alone - split it */ 385 386 last = strrchr (path_bkp, '/'); 387 if (last != NULL) { 388 if(last[1] == '\0') 389 /* if path ended in /, replace with `.' */ 390 a_params.in = "."; 391 else 392 a_params.in = last + 1; 393 while(last > path_bkp && *--last == '/'); 394 if(*last != '/' || last != path_bkp) { 395 *++last = '\0'; 396 dir = path_bkp; 397 } else 398 /* we got to the start, so this must be the root dir */ 399 dir = "/"; 400 } else { 401 /* file is relative to cdir */ 402 dir = "."; 403 a_params.in = path_bkp; 404 } 405 406 a_params.in_size = strlen (a_params.in) + 1; 407 a_params.out_size = maxsize; 408 409 ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0); 410 free (a_params.out); 411 if (ret < 0) { 412 free (path_bkp); 413 414 if (errno != EINVAL) 415 return ret; 416 else 417 /* if we get EINVAL this is probably not a mountpoint */ 418 return lstat (file, sb); 419 } 420 421 /* 422 * wow this was a mountpoint, lets cook the struct stat 423 * use . as a prototype 424 */ 425 426 ret = lstat (dir, sb); 427 free (path_bkp); 428 if (ret < 0) 429 return ret; 430 431 if (ino_last == sb->st_ino) 432 ino_counter++; 433 else { 434 ino_last = sb->st_ino; 435 ino_counter = 0; 436 } 437 sb->st_ino += ino_counter; 438 sb->st_nlink = 3; 439 440 return 0; 441 } 442 #endif /* KRB4 */ 443 return lstat (file, sb); 444 } 445 446 static void 447 list_files(FILE *out, const char **files, int n_files, int flags) 448 { 449 struct fileinfo *fi; 450 int i; 451 452 fi = calloc(n_files, sizeof(*fi)); 453 if (fi == NULL) { 454 sec_fprintf2(out, "ouf of memory\r\n"); 455 return; 456 } 457 for(i = 0; i < n_files; i++) { 458 if(lstat_file(files[i], &fi[i].st) < 0) { 459 sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno)); 460 fi[i].filename = NULL; 461 } else { 462 if((flags & LS_DIRS) == 0 && S_ISDIR(fi[i].st.st_mode)) { 463 if(n_files > 1) 464 sec_fprintf2(out, "%s:\r\n", files[i]); 465 list_dir(out, files[i], flags); 466 } else { 467 make_fileinfo(files[i], &fi[i], flags); 468 } 469 } 470 } 471 switch(SORT_MODE(flags)) { 472 case LS_SORT_NAME: 473 qsort(fi, n_files, sizeof(*fi), 474 (int (*)(const void*, const void*))compare_filename); 475 break; 476 case LS_SORT_MTIME: 477 qsort(fi, n_files, sizeof(*fi), 478 (int (*)(const void*, const void*))compare_mtime); 479 break; 480 case LS_SORT_SIZE: 481 qsort(fi, n_files, sizeof(*fi), 482 (int (*)(const void*, const void*))compare_size); 483 break; 484 } 485 if(DISP_MODE(flags) == LS_DISP_LONG) { 486 int max_inode = 0; 487 int max_bsize = 0; 488 int max_n_link = 0; 489 int max_user = 0; 490 int max_group = 0; 491 int max_size = 0; 492 int max_major = 0; 493 int max_minor = 0; 494 int max_date = 0; 495 for(i = 0; i < n_files; i++) { 496 if(fi[i].filename == NULL) 497 continue; 498 if(fi[i].inode > max_inode) 499 max_inode = fi[i].inode; 500 if(fi[i].bsize > max_bsize) 501 max_bsize = fi[i].bsize; 502 if(fi[i].n_link > max_n_link) 503 max_n_link = fi[i].n_link; 504 if(strlen(fi[i].user) > max_user) 505 max_user = strlen(fi[i].user); 506 if(strlen(fi[i].group) > max_group) 507 max_group = strlen(fi[i].group); 508 if(fi[i].major != NULL && strlen(fi[i].major) > max_major) 509 max_major = strlen(fi[i].major); 510 if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor) 511 max_minor = strlen(fi[i].minor); 512 if(fi[i].size != NULL && strlen(fi[i].size) > max_size) 513 max_size = strlen(fi[i].size); 514 if(strlen(fi[i].date) > max_date) 515 max_date = strlen(fi[i].date); 516 } 517 if(max_size < max_major + max_minor + 2) 518 max_size = max_major + max_minor + 2; 519 else if(max_size - max_minor - 2 > max_major) 520 max_major = max_size - max_minor - 2; 521 max_inode = log10(max_inode); 522 max_bsize = log10(max_bsize); 523 max_n_link = log10(max_n_link); 524 525 if(flags & LS_SORT_REVERSE) 526 for(i = n_files - 1; i >= 0; i--) 527 print_file(out, 528 flags, 529 &fi[i], 530 max_inode, 531 max_bsize, 532 max_n_link, 533 max_user, 534 max_group, 535 max_size, 536 max_major, 537 max_minor, 538 max_date); 539 else 540 for(i = 0; i < n_files; i++) 541 print_file(out, 542 flags, 543 &fi[i], 544 max_inode, 545 max_bsize, 546 max_n_link, 547 max_user, 548 max_group, 549 max_size, 550 max_major, 551 max_minor, 552 max_date); 553 } else if(DISP_MODE(flags) == LS_DISP_COLUMN || 554 DISP_MODE(flags) == LS_DISP_CROSS) { 555 int max_len = 0; 556 int num_files = n_files; 557 int columns; 558 int j; 559 for(i = 0; i < n_files; i++) { 560 if(fi[i].filename == NULL) { 561 num_files--; 562 continue; 563 } 564 if(strlen(fi[i].filename) > max_len) 565 max_len = strlen(fi[i].filename); 566 } 567 columns = 80 / (max_len + 1); /* get space between columns */ 568 max_len = 80 / columns; 569 if(DISP_MODE(flags) == LS_DISP_CROSS) { 570 for(i = 0, j = 0; i < n_files; i++) { 571 if(fi[i].filename == NULL) 572 continue; 573 sec_fprintf2(out, "%-*s", max_len, fi[i].filename); 574 j++; 575 if(j == columns) { 576 sec_fprintf2(out, "\r\n"); 577 j = 0; 578 } 579 } 580 if(j > 0) 581 sec_fprintf2(out, "\r\n"); 582 } else { 583 int skip = (num_files + columns - 1) / columns; 584 j = 0; 585 for(i = 0; i < skip; i++) { 586 for(j = i; j < n_files;) { 587 while(j < n_files && fi[j].filename == NULL) 588 j++; 589 sec_fprintf2(out, "%-*s", max_len, fi[j].filename); 590 j += skip; 591 } 592 sec_fprintf2(out, "\r\n"); 593 } 594 } 595 } else { 596 for(i = 0; i < n_files; i++) { 597 if(fi[i].filename == NULL) 598 continue; 599 sec_fprintf2(out, "%s\r\n", fi[i].filename); 600 } 601 } 602 for(i = 0; i < n_files; i++) 603 free_fileinfo(&fi[i]); 604 free(fi); 605 } 606 607 static void 608 free_files (char **files, int n) 609 { 610 int i; 611 612 for (i = 0; i < n; ++i) 613 free (files[i]); 614 free (files); 615 } 616 617 static void 618 list_dir(FILE *out, const char *directory, int flags) 619 { 620 DIR *d = opendir(directory); 621 struct dirent *ent; 622 char **files = NULL; 623 int n_files = 0; 624 625 if(d == NULL) { 626 sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno)); 627 return; 628 } 629 while((ent = readdir(d)) != NULL) { 630 void *tmp; 631 632 if(ent->d_name[0] == '.') { 633 if (flags & LS_IGNORE_DOT) 634 continue; 635 if (ent->d_name[1] == 0) /* Ignore . */ 636 continue; 637 if (ent->d_name[1] == '.' && ent->d_name[2] == 0) /* Ignore .. */ 638 continue; 639 } 640 tmp = realloc(files, (n_files + 1) * sizeof(*files)); 641 if (tmp == NULL) { 642 sec_fprintf2(out, "%s: out of memory\r\n", directory); 643 free_files (files, n_files); 644 closedir (d); 645 return; 646 } 647 files = tmp; 648 asprintf(&files[n_files], "%s/%s", directory, ent->d_name); 649 if (files[n_files] == NULL) { 650 sec_fprintf2(out, "%s: out of memory\r\n", directory); 651 free_files (files, n_files); 652 closedir (d); 653 return; 654 } 655 ++n_files; 656 } 657 closedir(d); 658 list_files(out, (const char**)files, n_files, flags | LS_DIRS); 659 } 660 661 void 662 builtin_ls(FILE *out, const char *file) 663 { 664 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG; 665 666 if(*file == '-') { 667 const char *p; 668 for(p = file + 1; *p; p++) { 669 switch(*p) { 670 case '1': 671 flags = (flags & ~LS_DISP_MODE); 672 break; 673 case 'a': 674 case 'A': 675 flags &= ~LS_IGNORE_DOT; 676 break; 677 case 'C': 678 flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN; 679 break; 680 case 'd': 681 flags |= LS_DIRS; 682 break; 683 case 'f': 684 flags = (flags & ~LS_SORT_MODE); 685 break; 686 case 'F': 687 flags |= LS_TYPE; 688 break; 689 case 'i': 690 flags |= LS_INODE; 691 break; 692 case 'l': 693 flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG; 694 break; 695 case 't': 696 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME; 697 break; 698 case 's': 699 flags |= LS_SIZE; 700 break; 701 case 'S': 702 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE; 703 break; 704 case 'r': 705 flags |= LS_SORT_REVERSE; 706 break; 707 case 'x': 708 flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS; 709 break; 710 } 711 } 712 file = "."; 713 } 714 list_files(out, &file, 1, flags); 715 sec_fflush(out); 716 } 717