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