1 /* 2 * Copyright (c) 1999 - 2001 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.23 2001/09/14 11:32:52 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 static void list_files(FILE *out, const char **files, int n_files, int flags); 53 static int parse_flags(const char *options); 54 55 int 56 main(int argc, char **argv) 57 { 58 int i = 1; 59 int flags; 60 if(argc > 1 && argv[1][0] == '-') { 61 flags = parse_flags(argv[1]); 62 i = 2; 63 } else 64 flags = parse_flags(NULL); 65 66 list_files(stdout, (const char **)argv + i, argc - i, flags); 67 return 0; 68 } 69 #endif 70 71 struct fileinfo { 72 struct stat st; 73 int inode; 74 int bsize; 75 char mode[11]; 76 int n_link; 77 char *user; 78 char *group; 79 char *size; 80 char *major; 81 char *minor; 82 char *date; 83 char *filename; 84 char *link; 85 }; 86 87 static void 88 free_fileinfo(struct fileinfo *f) 89 { 90 free(f->user); 91 free(f->group); 92 free(f->size); 93 free(f->major); 94 free(f->minor); 95 free(f->date); 96 free(f->filename); 97 free(f->link); 98 } 99 100 #define LS_DIRS (1 << 0) 101 #define LS_IGNORE_DOT (1 << 1) 102 #define LS_SORT_MODE (3 << 2) 103 #define SORT_MODE(f) ((f) & LS_SORT_MODE) 104 #define LS_SORT_NAME (1 << 2) 105 #define LS_SORT_MTIME (2 << 2) 106 #define LS_SORT_SIZE (3 << 2) 107 #define LS_SORT_REVERSE (1 << 4) 108 109 #define LS_SIZE (1 << 5) 110 #define LS_INODE (1 << 6) 111 #define LS_TYPE (1 << 7) 112 #define LS_DISP_MODE (3 << 8) 113 #define DISP_MODE(f) ((f) & LS_DISP_MODE) 114 #define LS_DISP_LONG (1 << 8) 115 #define LS_DISP_COLUMN (2 << 8) 116 #define LS_DISP_CROSS (3 << 8) 117 #define LS_SHOW_ALL (1 << 10) 118 #define LS_RECURSIVE (1 << 11) 119 #define LS_EXTRA_BLANK (1 << 12) 120 #define LS_SHOW_DIRNAME (1 << 13) 121 #define LS_DIR_FLAG (1 << 14) /* these files come via list_dir */ 122 123 #ifndef S_ISTXT 124 #define S_ISTXT S_ISVTX 125 #endif 126 127 #ifndef S_ISSOCK 128 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK) 129 #endif 130 131 #ifndef S_ISLNK 132 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK) 133 #endif 134 135 static size_t 136 block_convert(size_t blocks) 137 { 138 #ifdef S_BLKSIZE 139 return blocks * S_BLKSIZE / 1024; 140 #else 141 return blocks * 512 / 1024; 142 #endif 143 } 144 145 static void 146 make_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags) 147 { 148 char buf[128]; 149 int file_type = 0; 150 struct stat *st = &file->st; 151 152 file->inode = st->st_ino; 153 file->bsize = block_convert(st->st_blocks); 154 155 if(S_ISDIR(st->st_mode)) { 156 file->mode[0] = 'd'; 157 file_type = '/'; 158 } 159 else if(S_ISCHR(st->st_mode)) 160 file->mode[0] = 'c'; 161 else if(S_ISBLK(st->st_mode)) 162 file->mode[0] = 'b'; 163 else if(S_ISREG(st->st_mode)) { 164 file->mode[0] = '-'; 165 if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 166 file_type = '*'; 167 } 168 else if(S_ISFIFO(st->st_mode)) { 169 file->mode[0] = 'p'; 170 file_type = '|'; 171 } 172 else if(S_ISLNK(st->st_mode)) { 173 file->mode[0] = 'l'; 174 file_type = '@'; 175 } 176 else if(S_ISSOCK(st->st_mode)) { 177 file->mode[0] = 's'; 178 file_type = '='; 179 } 180 #ifdef S_ISWHT 181 else if(S_ISWHT(st->st_mode)) { 182 file->mode[0] = 'w'; 183 file_type = '%'; 184 } 185 #endif 186 else 187 file->mode[0] = '?'; 188 { 189 char *x[] = { "---", "--x", "-w-", "-wx", 190 "r--", "r-x", "rw-", "rwx" }; 191 strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]); 192 strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]); 193 strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]); 194 if((st->st_mode & S_ISUID)) { 195 if((st->st_mode & S_IXUSR)) 196 file->mode[3] = 's'; 197 else 198 file->mode[3] = 'S'; 199 } 200 if((st->st_mode & S_ISGID)) { 201 if((st->st_mode & S_IXGRP)) 202 file->mode[6] = 's'; 203 else 204 file->mode[6] = 'S'; 205 } 206 if((st->st_mode & S_ISTXT)) { 207 if((st->st_mode & S_IXOTH)) 208 file->mode[9] = 't'; 209 else 210 file->mode[9] = 'T'; 211 } 212 } 213 file->n_link = st->st_nlink; 214 { 215 struct passwd *pwd; 216 pwd = getpwuid(st->st_uid); 217 if(pwd == NULL) 218 asprintf(&file->user, "%u", (unsigned)st->st_uid); 219 else 220 file->user = strdup(pwd->pw_name); 221 } 222 { 223 struct group *grp; 224 grp = getgrgid(st->st_gid); 225 if(grp == NULL) 226 asprintf(&file->group, "%u", (unsigned)st->st_gid); 227 else 228 file->group = strdup(grp->gr_name); 229 } 230 231 if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 232 #if defined(major) && defined(minor) 233 asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)); 234 asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)); 235 #else 236 /* Don't want to use the DDI/DKI crap. */ 237 asprintf(&file->major, "%u", (unsigned)st->st_rdev); 238 asprintf(&file->minor, "%u", 0); 239 #endif 240 } else 241 asprintf(&file->size, "%lu", (unsigned long)st->st_size); 242 243 { 244 time_t t = time(NULL); 245 time_t mtime = st->st_mtime; 246 struct tm *tm = localtime(&mtime); 247 if((t - mtime > 6*30*24*60*60) || 248 (mtime - t > 6*30*24*60*60)) 249 strftime(buf, sizeof(buf), "%b %e %Y", tm); 250 else 251 strftime(buf, sizeof(buf), "%b %e %H:%M", tm); 252 file->date = strdup(buf); 253 } 254 { 255 const char *p = strrchr(filename, '/'); 256 if(p) 257 p++; 258 else 259 p = filename; 260 if((flags & LS_TYPE) && file_type != 0) 261 asprintf(&file->filename, "%s%c", p, file_type); 262 else 263 file->filename = strdup(p); 264 } 265 if(S_ISLNK(st->st_mode)) { 266 int n; 267 n = readlink((char *)filename, buf, sizeof(buf)); 268 if(n >= 0) { 269 buf[n] = '\0'; 270 file->link = strdup(buf); 271 } else 272 sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno)); 273 } 274 } 275 276 static void 277 print_file(FILE *out, 278 int flags, 279 struct fileinfo *f, 280 int max_inode, 281 int max_bsize, 282 int max_n_link, 283 int max_user, 284 int max_group, 285 int max_size, 286 int max_major, 287 int max_minor, 288 int max_date) 289 { 290 if(f->filename == NULL) 291 return; 292 293 if(flags & LS_INODE) { 294 sec_fprintf2(out, "%*d", max_inode, f->inode); 295 sec_fprintf2(out, " "); 296 } 297 if(flags & LS_SIZE) { 298 sec_fprintf2(out, "%*d", max_bsize, f->bsize); 299 sec_fprintf2(out, " "); 300 } 301 sec_fprintf2(out, "%s", f->mode); 302 sec_fprintf2(out, " "); 303 sec_fprintf2(out, "%*d", max_n_link, f->n_link); 304 sec_fprintf2(out, " "); 305 sec_fprintf2(out, "%-*s", max_user, f->user); 306 sec_fprintf2(out, " "); 307 sec_fprintf2(out, "%-*s", max_group, f->group); 308 sec_fprintf2(out, " "); 309 if(f->major != NULL && f->minor != NULL) 310 sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor); 311 else 312 sec_fprintf2(out, "%*s", max_size, f->size); 313 sec_fprintf2(out, " "); 314 sec_fprintf2(out, "%*s", max_date, f->date); 315 sec_fprintf2(out, " "); 316 sec_fprintf2(out, "%s", f->filename); 317 if(f->link) 318 sec_fprintf2(out, " -> %s", f->link); 319 sec_fprintf2(out, "\r\n"); 320 } 321 322 static int 323 compare_filename(struct fileinfo *a, struct fileinfo *b) 324 { 325 if(a->filename == NULL) 326 return 1; 327 if(b->filename == NULL) 328 return -1; 329 return strcmp(a->filename, b->filename); 330 } 331 332 static int 333 compare_mtime(struct fileinfo *a, struct fileinfo *b) 334 { 335 if(a->filename == NULL) 336 return 1; 337 if(b->filename == NULL) 338 return -1; 339 return b->st.st_mtime - a->st.st_mtime; 340 } 341 342 static int 343 compare_size(struct fileinfo *a, struct fileinfo *b) 344 { 345 if(a->filename == NULL) 346 return 1; 347 if(b->filename == NULL) 348 return -1; 349 return b->st.st_size - a->st.st_size; 350 } 351 352 static void 353 list_dir(FILE *out, const char *directory, int flags); 354 355 static int 356 log10(int num) 357 { 358 int i = 1; 359 while(num > 10) { 360 i++; 361 num /= 10; 362 } 363 return i; 364 } 365 366 /* 367 * Operate as lstat but fake up entries for AFS mount points so we don't 368 * have to fetch them. 369 */ 370 371 #ifdef KRB4 372 static int do_the_afs_dance = 1; 373 #endif 374 375 static int 376 lstat_file (const char *file, struct stat *sb) 377 { 378 #ifdef KRB4 379 if (do_the_afs_dance && 380 k_hasafs() 381 && strcmp(file, ".") 382 && strcmp(file, "..") 383 && strcmp(file, "/")) 384 { 385 struct ViceIoctl a_params; 386 char *dir, *last; 387 char *path_bkp; 388 static ino_t ino_counter = 0, ino_last = 0; 389 int ret; 390 const int maxsize = 2048; 391 392 path_bkp = strdup (file); 393 if (path_bkp == NULL) 394 return -1; 395 396 a_params.out = malloc (maxsize); 397 if (a_params.out == NULL) { 398 free (path_bkp); 399 return -1; 400 } 401 402 /* If path contains more than the filename alone - split it */ 403 404 last = strrchr (path_bkp, '/'); 405 if (last != NULL) { 406 if(last[1] == '\0') 407 /* if path ended in /, replace with `.' */ 408 a_params.in = "."; 409 else 410 a_params.in = last + 1; 411 while(last > path_bkp && *--last == '/'); 412 if(*last != '/' || last != path_bkp) { 413 *++last = '\0'; 414 dir = path_bkp; 415 } else 416 /* we got to the start, so this must be the root dir */ 417 dir = "/"; 418 } else { 419 /* file is relative to cdir */ 420 dir = "."; 421 a_params.in = path_bkp; 422 } 423 424 a_params.in_size = strlen (a_params.in) + 1; 425 a_params.out_size = maxsize; 426 427 ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0); 428 free (a_params.out); 429 if (ret < 0) { 430 free (path_bkp); 431 432 if (errno != EINVAL) 433 return ret; 434 else 435 /* if we get EINVAL this is probably not a mountpoint */ 436 return lstat (file, sb); 437 } 438 439 /* 440 * wow this was a mountpoint, lets cook the struct stat 441 * use . as a prototype 442 */ 443 444 ret = lstat (dir, sb); 445 free (path_bkp); 446 if (ret < 0) 447 return ret; 448 449 if (ino_last == sb->st_ino) 450 ino_counter++; 451 else { 452 ino_last = sb->st_ino; 453 ino_counter = 0; 454 } 455 sb->st_ino += ino_counter; 456 sb->st_nlink = 3; 457 458 return 0; 459 } 460 #endif /* KRB4 */ 461 return lstat (file, sb); 462 } 463 464 #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \ 465 ((X)[1] == '.' && (X)[2] == '\0'))) 466 467 static void 468 list_files(FILE *out, const char **files, int n_files, int flags) 469 { 470 struct fileinfo *fi; 471 int i; 472 int *dirs = NULL; 473 size_t total_blocks = 0; 474 int n_print = 0; 475 476 if(n_files > 1) 477 flags |= LS_SHOW_DIRNAME; 478 479 fi = calloc(n_files, sizeof(*fi)); 480 if (fi == NULL) { 481 sec_fprintf2(out, "ouf of memory\r\n"); 482 return; 483 } 484 for(i = 0; i < n_files; i++) { 485 if(lstat_file(files[i], &fi[i].st) < 0) { 486 sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno)); 487 fi[i].filename = NULL; 488 } else { 489 int include_in_list = 1; 490 total_blocks += block_convert(fi[i].st.st_blocks); 491 if(S_ISDIR(fi[i].st.st_mode)) { 492 if(dirs == NULL) 493 dirs = calloc(n_files, sizeof(*dirs)); 494 if(dirs == NULL) { 495 sec_fprintf2(out, "%s: %s\r\n", 496 files[i], strerror(errno)); 497 goto out; 498 } 499 dirs[i] = 1; 500 if((flags & LS_DIRS) == 0) 501 include_in_list = 0; 502 } 503 if(include_in_list) { 504 make_fileinfo(out, files[i], &fi[i], flags); 505 n_print++; 506 } 507 } 508 } 509 switch(SORT_MODE(flags)) { 510 case LS_SORT_NAME: 511 qsort(fi, n_files, sizeof(*fi), 512 (int (*)(const void*, const void*))compare_filename); 513 break; 514 case LS_SORT_MTIME: 515 qsort(fi, n_files, sizeof(*fi), 516 (int (*)(const void*, const void*))compare_mtime); 517 break; 518 case LS_SORT_SIZE: 519 qsort(fi, n_files, sizeof(*fi), 520 (int (*)(const void*, const void*))compare_size); 521 break; 522 } 523 if(DISP_MODE(flags) == LS_DISP_LONG) { 524 int max_inode = 0; 525 int max_bsize = 0; 526 int max_n_link = 0; 527 int max_user = 0; 528 int max_group = 0; 529 int max_size = 0; 530 int max_major = 0; 531 int max_minor = 0; 532 int max_date = 0; 533 for(i = 0; i < n_files; i++) { 534 if(fi[i].filename == NULL) 535 continue; 536 if(fi[i].inode > max_inode) 537 max_inode = fi[i].inode; 538 if(fi[i].bsize > max_bsize) 539 max_bsize = fi[i].bsize; 540 if(fi[i].n_link > max_n_link) 541 max_n_link = fi[i].n_link; 542 if(strlen(fi[i].user) > max_user) 543 max_user = strlen(fi[i].user); 544 if(strlen(fi[i].group) > max_group) 545 max_group = strlen(fi[i].group); 546 if(fi[i].major != NULL && strlen(fi[i].major) > max_major) 547 max_major = strlen(fi[i].major); 548 if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor) 549 max_minor = strlen(fi[i].minor); 550 if(fi[i].size != NULL && strlen(fi[i].size) > max_size) 551 max_size = strlen(fi[i].size); 552 if(strlen(fi[i].date) > max_date) 553 max_date = strlen(fi[i].date); 554 } 555 if(max_size < max_major + max_minor + 2) 556 max_size = max_major + max_minor + 2; 557 else if(max_size - max_minor - 2 > max_major) 558 max_major = max_size - max_minor - 2; 559 max_inode = log10(max_inode); 560 max_bsize = log10(max_bsize); 561 max_n_link = log10(max_n_link); 562 563 if(n_print > 0) 564 sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks); 565 if(flags & LS_SORT_REVERSE) 566 for(i = n_files - 1; i >= 0; i--) 567 print_file(out, 568 flags, 569 &fi[i], 570 max_inode, 571 max_bsize, 572 max_n_link, 573 max_user, 574 max_group, 575 max_size, 576 max_major, 577 max_minor, 578 max_date); 579 else 580 for(i = 0; i < n_files; i++) 581 print_file(out, 582 flags, 583 &fi[i], 584 max_inode, 585 max_bsize, 586 max_n_link, 587 max_user, 588 max_group, 589 max_size, 590 max_major, 591 max_minor, 592 max_date); 593 } else if(DISP_MODE(flags) == LS_DISP_COLUMN || 594 DISP_MODE(flags) == LS_DISP_CROSS) { 595 int max_len = 0; 596 int size_len = 0; 597 int num_files = n_files; 598 int columns; 599 int j; 600 for(i = 0; i < n_files; i++) { 601 if(fi[i].filename == NULL) { 602 num_files--; 603 continue; 604 } 605 if(strlen(fi[i].filename) > max_len) 606 max_len = strlen(fi[i].filename); 607 if(log10(fi[i].bsize) > size_len) 608 size_len = log10(fi[i].bsize); 609 } 610 if(num_files == 0) 611 goto next; 612 if(flags & LS_SIZE) { 613 columns = 80 / (size_len + 1 + max_len + 1); 614 max_len = 80 / columns - size_len - 1; 615 } else { 616 columns = 80 / (max_len + 1); /* get space between columns */ 617 max_len = 80 / columns; 618 } 619 if(flags & LS_SIZE) 620 sec_fprintf2(out, "total %lu\r\n", 621 (unsigned long)total_blocks); 622 if(DISP_MODE(flags) == LS_DISP_CROSS) { 623 for(i = 0, j = 0; i < n_files; i++) { 624 if(fi[i].filename == NULL) 625 continue; 626 if(flags & LS_SIZE) 627 sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize, 628 max_len, fi[i].filename); 629 else 630 sec_fprintf2(out, "%-*s", max_len, fi[i].filename); 631 j++; 632 if(j == columns) { 633 sec_fprintf2(out, "\r\n"); 634 j = 0; 635 } 636 } 637 if(j > 0) 638 sec_fprintf2(out, "\r\n"); 639 } else { 640 int skip = (num_files + columns - 1) / columns; 641 j = 0; 642 for(i = 0; i < skip; i++) { 643 for(j = i; j < n_files;) { 644 while(j < n_files && fi[j].filename == NULL) 645 j++; 646 if(flags & LS_SIZE) 647 sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize, 648 max_len, fi[j].filename); 649 else 650 sec_fprintf2(out, "%-*s", max_len, fi[j].filename); 651 j += skip; 652 } 653 sec_fprintf2(out, "\r\n"); 654 } 655 } 656 } else { 657 for(i = 0; i < n_files; i++) { 658 if(fi[i].filename == NULL) 659 continue; 660 sec_fprintf2(out, "%s\r\n", fi[i].filename); 661 } 662 } 663 next: 664 if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) { 665 for(i = 0; i < n_files; i++) { 666 if(dirs[i]) { 667 const char *p = strrchr(files[i], '/'); 668 if(p == NULL) 669 p = files[i]; 670 else 671 p++; 672 if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) { 673 if((flags & LS_SHOW_DIRNAME)) { 674 if ((flags & LS_EXTRA_BLANK)) 675 sec_fprintf2(out, "\r\n"); 676 sec_fprintf2(out, "%s:\r\n", files[i]); 677 } 678 list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK); 679 } 680 } 681 } 682 } 683 out: 684 for(i = 0; i < n_files; i++) 685 free_fileinfo(&fi[i]); 686 free(fi); 687 if(dirs != NULL) 688 free(dirs); 689 } 690 691 static void 692 free_files (char **files, int n) 693 { 694 int i; 695 696 for (i = 0; i < n; ++i) 697 free (files[i]); 698 free (files); 699 } 700 701 static int 702 hide_file(const char *filename, int flags) 703 { 704 if(filename[0] != '.') 705 return 0; 706 if((flags & LS_IGNORE_DOT)) 707 return 1; 708 if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) { 709 if((flags & LS_SHOW_ALL)) 710 return 0; 711 else 712 return 1; 713 } 714 return 0; 715 } 716 717 static void 718 list_dir(FILE *out, const char *directory, int flags) 719 { 720 DIR *d = opendir(directory); 721 struct dirent *ent; 722 char **files = NULL; 723 int n_files = 0; 724 725 if(d == NULL) { 726 sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno)); 727 return; 728 } 729 while((ent = readdir(d)) != NULL) { 730 void *tmp; 731 732 if(hide_file(ent->d_name, flags)) 733 continue; 734 tmp = realloc(files, (n_files + 1) * sizeof(*files)); 735 if (tmp == NULL) { 736 sec_fprintf2(out, "%s: out of memory\r\n", directory); 737 free_files (files, n_files); 738 closedir (d); 739 return; 740 } 741 files = tmp; 742 asprintf(&files[n_files], "%s/%s", directory, ent->d_name); 743 if (files[n_files] == NULL) { 744 sec_fprintf2(out, "%s: out of memory\r\n", directory); 745 free_files (files, n_files); 746 closedir (d); 747 return; 748 } 749 ++n_files; 750 } 751 closedir(d); 752 list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG); 753 } 754 755 static int 756 parse_flags(const char *options) 757 { 758 #ifdef TEST 759 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN; 760 #else 761 int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG; 762 #endif 763 764 const char *p; 765 if(options == NULL || *options != '-') 766 return flags; 767 for(p = options + 1; *p; p++) { 768 switch(*p) { 769 case '1': 770 flags = (flags & ~LS_DISP_MODE); 771 break; 772 case 'a': 773 flags |= LS_SHOW_ALL; 774 /*FALLTHROUGH*/ 775 case 'A': 776 flags &= ~LS_IGNORE_DOT; 777 break; 778 case 'C': 779 flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN; 780 break; 781 case 'd': 782 flags |= LS_DIRS; 783 break; 784 case 'f': 785 flags = (flags & ~LS_SORT_MODE); 786 break; 787 case 'F': 788 flags |= LS_TYPE; 789 break; 790 case 'i': 791 flags |= LS_INODE; 792 break; 793 case 'l': 794 flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG; 795 break; 796 case 'r': 797 flags |= LS_SORT_REVERSE; 798 break; 799 case 'R': 800 flags |= LS_RECURSIVE; 801 break; 802 case 's': 803 flags |= LS_SIZE; 804 break; 805 case 'S': 806 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE; 807 break; 808 case 't': 809 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME; 810 break; 811 case 'x': 812 flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS; 813 break; 814 /* these are a bunch of unimplemented flags from BSD ls */ 815 case 'k': /* display sizes in kB */ 816 case 'c': /* last change time */ 817 case 'L': /* list symlink target */ 818 case 'm': /* stream output */ 819 case 'o': /* BSD file flags */ 820 case 'p': /* display / after directories */ 821 case 'q': /* print non-graphic characters */ 822 case 'u': /* use last access time */ 823 case 'T': /* display complete time */ 824 case 'W': /* include whiteouts */ 825 break; 826 } 827 } 828 return flags; 829 } 830 831 void 832 builtin_ls(FILE *out, const char *file) 833 { 834 int flags; 835 836 if(*file == '-') { 837 flags = parse_flags(file); 838 file = "."; 839 } else 840 flags = parse_flags(""); 841 842 list_files(out, &file, 1, flags); 843 sec_fflush(out); 844 } 845