/crypto/heimdal/appl/ftp/ftpd/ls.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 891 lines · 785 code · 59 blank · 47 comment · 252 complexity · 53212496a7068b000d731cbc3fd6fc0e MD5 · raw file

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