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