ls.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * tiny-ls.c version 0.1.0: A minimalist 'ls'
  4. * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. */
  20. /*
  21. * To achieve a small memory footprint, this version of 'ls' doesn't do any
  22. * file sorting, and only has the most essential command line switches
  23. * (i.e., the ones I couldn't live without :-) All features which involve
  24. * linking in substantial chunks of libc can be disabled.
  25. *
  26. * Although I don't really want to add new features to this program to
  27. * keep it small, I *am* interested to receive bug fixes and ways to make
  28. * it more portable.
  29. *
  30. * KNOWN BUGS:
  31. * 1. ls -l of a directory doesn't give "total <blocks>" header
  32. * 2. ls of a symlink to a directory doesn't list directory contents
  33. * 3. hidden files can make column width too large
  34. *
  35. * NON-OPTIMAL BEHAVIOUR:
  36. * 1. autowidth reads directories twice
  37. * 2. if you do a short directory listing without filetype characters
  38. * appended, there's no need to stat each one
  39. * PORTABILITY:
  40. * 1. requires lstat (BSD) - how do you do it without?
  41. */
  42. enum {
  43. TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
  44. COLUMN_GAP = 2, /* includes the file type char */
  45. };
  46. /************************************************************************/
  47. #include <sys/types.h>
  48. #include <sys/stat.h>
  49. #include <stdio.h>
  50. #include <unistd.h>
  51. #include <dirent.h>
  52. #include <errno.h>
  53. #include <stdio.h>
  54. #include <string.h>
  55. #include <stdlib.h>
  56. #include <fcntl.h>
  57. #include <signal.h>
  58. #include <termios.h>
  59. #include <getopt.h> /* struct option */
  60. #include <sys/ioctl.h>
  61. #include <sys/sysmacros.h> /* major() and minor() */
  62. #include "busybox.h"
  63. #ifdef CONFIG_SELINUX
  64. #include <selinux/selinux.h> /* for is_selinux_enabled() */
  65. #endif
  66. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  67. #include <time.h>
  68. #endif
  69. /* what is the overall style of the listing */
  70. #define STYLE_AUTO (0)
  71. #define STYLE_COLUMNS (1U<<21) /* fill columns */
  72. #define STYLE_LONG (2U<<21) /* one record per line, extended info */
  73. #define STYLE_SINGLE (3U<<21) /* one record per line */
  74. #define STYLE_MASK STYLE_SINGLE
  75. #define STYLE_ONE_RECORD_FLAG STYLE_LONG
  76. /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
  77. /* what file information will be listed */
  78. #define LIST_INO (1U<<0)
  79. #define LIST_BLOCKS (1U<<1)
  80. #define LIST_MODEBITS (1U<<2)
  81. #define LIST_NLINKS (1U<<3)
  82. #define LIST_ID_NAME (1U<<4)
  83. #define LIST_ID_NUMERIC (1U<<5)
  84. #define LIST_CONTEXT (1U<<6)
  85. #define LIST_SIZE (1U<<7)
  86. #define LIST_DEV (1U<<8)
  87. #define LIST_DATE_TIME (1U<<9)
  88. #define LIST_FULLTIME (1U<<10)
  89. #define LIST_FILENAME (1U<<11)
  90. #define LIST_SYMLINK (1U<<12)
  91. #define LIST_FILETYPE (1U<<13)
  92. #define LIST_EXEC (1U<<14)
  93. #define LIST_MASK ((LIST_EXEC << 1) - 1)
  94. /* what files will be displayed */
  95. /* TODO -- We may be able to make DISP_NORMAL 0 to save a bit slot. */
  96. #define DISP_NORMAL (1U<<14) /* show normal filenames */
  97. #define DISP_DIRNAME (1U<<15) /* 2 or more items? label directories */
  98. #define DISP_HIDDEN (1U<<16) /* show filenames starting with . */
  99. #define DISP_DOT (1U<<17) /* show . and .. */
  100. #define DISP_NOLIST (1U<<18) /* show directory as itself, not contents */
  101. #define DISP_RECURSIVE (1U<<19) /* show directory and everything below it */
  102. #define DISP_ROWS (1U<<20) /* print across rows */
  103. #define DISP_MASK (((DISP_ROWS << 1) - 1) & ~(DISP_NORMAL - 1))
  104. #ifdef CONFIG_FEATURE_LS_SORTFILES
  105. /* how will the files be sorted */
  106. #define SORT_ORDER_FORWARD 0 /* sort in reverse order */
  107. #define SORT_ORDER_REVERSE (1U<<27) /* sort in reverse order */
  108. #define SORT_NAME 0 /* sort by file name */
  109. #define SORT_SIZE (1U<<28) /* sort by file size */
  110. #define SORT_ATIME (2U<<28) /* sort by last access time */
  111. #define SORT_CTIME (3U<<28) /* sort by last change time */
  112. #define SORT_MTIME (4U<<28) /* sort by last modification time */
  113. #define SORT_VERSION (5U<<28) /* sort by version */
  114. #define SORT_EXT (6U<<28) /* sort by file name extension */
  115. #define SORT_DIR (7U<<28) /* sort by file or directory */
  116. #define SORT_MASK (7U<<28)
  117. #endif
  118. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  119. /* which of the three times will be used */
  120. #define TIME_MOD 0
  121. #define TIME_CHANGE (1U<<23)
  122. #define TIME_ACCESS (1U<<24)
  123. #define TIME_MASK (3U<<23)
  124. #endif
  125. #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
  126. #define FOLLOW_LINKS (1U<<25)
  127. #endif
  128. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  129. #define LS_DISP_HR (1U<<26)
  130. #endif
  131. #define LIST_SHORT (LIST_FILENAME)
  132. #define LIST_ISHORT (LIST_INO | LIST_FILENAME)
  133. #define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
  134. LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK)
  135. #define LIST_ILONG (LIST_INO | LIST_LONG)
  136. #define SPLIT_DIR 1
  137. #define SPLIT_FILE 0
  138. #define SPLIT_SUBDIR 2
  139. #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
  140. #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
  141. #if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
  142. # define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
  143. #endif
  144. /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
  145. #ifdef CONFIG_FEATURE_LS_COLOR
  146. static int show_color = 0;
  147. /* long option entry used only for --color, which has no short option
  148. * equivalent. */
  149. static const struct option ls_color_opt[] =
  150. {
  151. {"color", optional_argument, NULL, 1},
  152. {NULL, 0, NULL, 0}
  153. };
  154. #define COLOR(mode) ("\000\043\043\043\042\000\043\043"\
  155. "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
  156. #define ATTR(mode) ("\00\00\01\00\01\00\01\00"\
  157. "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
  158. #endif
  159. /*
  160. * a directory entry and its stat info are stored here
  161. */
  162. struct dnode { /* the basic node */
  163. char *name; /* the dir entry name */
  164. char *fullname; /* the dir entry name */
  165. int allocated;
  166. struct stat dstat; /* the file stat info */
  167. #ifdef CONFIG_SELINUX
  168. security_context_t sid;
  169. #endif
  170. struct dnode *next; /* point at the next node */
  171. };
  172. typedef struct dnode dnode_t;
  173. static struct dnode **list_dir(const char *);
  174. static struct dnode **dnalloc(int);
  175. static int list_single(struct dnode *);
  176. static unsigned int all_fmt;
  177. #ifdef CONFIG_FEATURE_AUTOWIDTH
  178. static int terminal_width = TERMINAL_WIDTH;
  179. static unsigned short tabstops = COLUMN_GAP;
  180. #else
  181. #define tabstops COLUMN_GAP
  182. #define terminal_width TERMINAL_WIDTH
  183. #endif
  184. static int status = EXIT_SUCCESS;
  185. static struct dnode *my_stat(char *fullname, char *name)
  186. {
  187. struct stat dstat;
  188. struct dnode *cur;
  189. #ifdef CONFIG_SELINUX
  190. security_context_t sid=NULL;
  191. #endif
  192. int rc;
  193. #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
  194. if (all_fmt & FOLLOW_LINKS) {
  195. #ifdef CONFIG_SELINUX
  196. if (is_selinux_enabled()) {
  197. rc=0; /* Set the number which means success before hand. */
  198. rc = getfilecon(fullname,&sid);
  199. }
  200. #endif
  201. rc = stat(fullname, &dstat);
  202. if(rc) {
  203. bb_perror_msg("%s", fullname);
  204. status = EXIT_FAILURE;
  205. return 0;
  206. }
  207. } else
  208. #endif
  209. {
  210. #ifdef CONFIG_SELINUX
  211. if (is_selinux_enabled()) {
  212. rc=0; /* Set the number which means success before hand. */
  213. rc = lgetfilecon(fullname,&sid);
  214. }
  215. #endif
  216. rc = lstat(fullname, &dstat);
  217. if(rc) {
  218. bb_perror_msg("%s", fullname);
  219. status = EXIT_FAILURE;
  220. return 0;
  221. }
  222. }
  223. cur = (struct dnode *) xmalloc(sizeof(struct dnode));
  224. cur->fullname = fullname;
  225. cur->name = name;
  226. cur->dstat = dstat;
  227. #ifdef CONFIG_SELINUX
  228. cur->sid = sid;
  229. #endif
  230. return cur;
  231. }
  232. /*----------------------------------------------------------------------*/
  233. #ifdef CONFIG_FEATURE_LS_COLOR
  234. static char fgcolor(mode_t mode)
  235. {
  236. /* Check wheter the file is existing (if so, color it red!) */
  237. if (errno == ENOENT) {
  238. return '\037';
  239. }
  240. if (LIST_EXEC && S_ISREG(mode)
  241. && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
  242. return COLOR(0xF000); /* File is executable ... */
  243. return COLOR(mode);
  244. }
  245. /*----------------------------------------------------------------------*/
  246. static char bgcolor(mode_t mode)
  247. {
  248. if (LIST_EXEC && S_ISREG(mode)
  249. && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
  250. return ATTR(0xF000); /* File is executable ... */
  251. return ATTR(mode);
  252. }
  253. #endif
  254. /*----------------------------------------------------------------------*/
  255. #if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
  256. static char append_char(mode_t mode)
  257. {
  258. if (!(all_fmt & LIST_FILETYPE))
  259. return '\0';
  260. if ((all_fmt & LIST_EXEC) && S_ISREG(mode)
  261. && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
  262. return '*';
  263. return APPCHAR(mode);
  264. }
  265. #endif
  266. /*----------------------------------------------------------------------*/
  267. #define countdirs(A,B) count_dirs((A), (B), 1)
  268. #define countsubdirs(A,B) count_dirs((A), (B), 0)
  269. static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
  270. {
  271. int i, dirs;
  272. if (dn == NULL || nfiles < 1)
  273. return (0);
  274. dirs = 0;
  275. for (i = 0; i < nfiles; i++) {
  276. if (S_ISDIR(dn[i]->dstat.st_mode)
  277. && (notsubdirs ||
  278. ((dn[i]->name[0] != '.') || (dn[i]->name[1]
  279. && ((dn[i]->name[1] != '.')
  280. || dn[i]->name[2])))))
  281. dirs++;
  282. }
  283. return (dirs);
  284. }
  285. static int countfiles(struct dnode **dnp)
  286. {
  287. int nfiles;
  288. struct dnode *cur;
  289. if (dnp == NULL)
  290. return (0);
  291. nfiles = 0;
  292. for (cur = dnp[0]; cur->next != NULL; cur = cur->next)
  293. nfiles++;
  294. nfiles++;
  295. return (nfiles);
  296. }
  297. /* get memory to hold an array of pointers */
  298. static struct dnode **dnalloc(int num)
  299. {
  300. struct dnode **p;
  301. if (num < 1)
  302. return (NULL);
  303. p = (struct dnode **) xcalloc((size_t) num, (size_t) (sizeof(struct dnode *)));
  304. return (p);
  305. }
  306. #ifdef CONFIG_FEATURE_LS_RECURSIVE
  307. static void dfree(struct dnode **dnp, int nfiles)
  308. {
  309. int i;
  310. if (dnp == NULL)
  311. return;
  312. for (i = 0; i < nfiles; i++) {
  313. struct dnode *cur = dnp[i];
  314. if(cur->allocated)
  315. free(cur->fullname); /* free the filename */
  316. free(cur); /* free the dnode */
  317. }
  318. free(dnp); /* free the array holding the dnode pointers */
  319. }
  320. #endif
  321. static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
  322. {
  323. int dncnt, i, d;
  324. struct dnode **dnp;
  325. if (dn == NULL || nfiles < 1)
  326. return (NULL);
  327. /* count how many dirs and regular files there are */
  328. if (which == SPLIT_SUBDIR)
  329. dncnt = countsubdirs(dn, nfiles);
  330. else {
  331. dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
  332. if (which == SPLIT_FILE)
  333. dncnt = nfiles - dncnt; /* looking for files */
  334. }
  335. /* allocate a file array and a dir array */
  336. dnp = dnalloc(dncnt);
  337. /* copy the entrys into the file or dir array */
  338. for (d = i = 0; i < nfiles; i++) {
  339. if (S_ISDIR(dn[i]->dstat.st_mode)) {
  340. if (which & (SPLIT_DIR|SPLIT_SUBDIR)) {
  341. if ((which & SPLIT_DIR)
  342. || ((dn[i]->name[0] != '.')
  343. || (dn[i]->name[1]
  344. && ((dn[i]->name[1] != '.')
  345. || dn[i]->name[2])))) {
  346. dnp[d++] = dn[i];
  347. }
  348. }
  349. } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
  350. dnp[d++] = dn[i];
  351. }
  352. }
  353. return (dnp);
  354. }
  355. /*----------------------------------------------------------------------*/
  356. #ifdef CONFIG_FEATURE_LS_SORTFILES
  357. static int sortcmp(struct dnode *d1, struct dnode *d2)
  358. {
  359. unsigned int sort_opts = all_fmt & SORT_MASK;
  360. int dif;
  361. dif = 0; /* assume SORT_NAME */
  362. if (sort_opts == SORT_SIZE) {
  363. dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
  364. } else if (sort_opts == SORT_ATIME) {
  365. dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
  366. } else if (sort_opts == SORT_CTIME) {
  367. dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
  368. } else if (sort_opts == SORT_MTIME) {
  369. dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
  370. } else if (sort_opts == SORT_DIR) {
  371. dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
  372. /* } else if (sort_opts == SORT_VERSION) { */
  373. /* } else if (sort_opts == SORT_EXT) { */
  374. }
  375. if (dif == 0) {
  376. /* sort by name- may be a tie_breaker for time or size cmp */
  377. #ifdef CONFIG_LOCALE_SUPPORT
  378. dif = strcoll(d1->name, d2->name);
  379. #else
  380. dif = strcmp(d1->name, d2->name);
  381. #endif
  382. }
  383. if (all_fmt & SORT_ORDER_REVERSE) {
  384. dif = -dif;
  385. }
  386. return (dif);
  387. }
  388. /*----------------------------------------------------------------------*/
  389. static void shellsort(struct dnode **dn, int size)
  390. {
  391. struct dnode *temp;
  392. int gap, i, j;
  393. /* shell short the array */
  394. if (dn == NULL || size < 2)
  395. return;
  396. for (gap = size / 2; gap > 0; gap /= 2) {
  397. for (i = gap; i < size; i++) {
  398. for (j = i - gap; j >= 0; j -= gap) {
  399. if (sortcmp(dn[j], dn[j + gap]) <= 0)
  400. break;
  401. /* they are out of order, swap them */
  402. temp = dn[j];
  403. dn[j] = dn[j + gap];
  404. dn[j + gap] = temp;
  405. }
  406. }
  407. }
  408. }
  409. #endif
  410. /*----------------------------------------------------------------------*/
  411. static void showfiles(struct dnode **dn, int nfiles)
  412. {
  413. int i, ncols, nrows, row, nc;
  414. int column = 0;
  415. int nexttab = 0;
  416. int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
  417. if (dn == NULL || nfiles < 1)
  418. return;
  419. if (all_fmt & STYLE_ONE_RECORD_FLAG) {
  420. ncols = 1;
  421. } else {
  422. /* find the longest file name- use that as the column width */
  423. for (i = 0; i < nfiles; i++) {
  424. int len = strlen(dn[i]->name) +
  425. #ifdef CONFIG_SELINUX
  426. ((all_fmt & LIST_CONTEXT) ? 33 : 0) +
  427. #endif
  428. ((all_fmt & LIST_INO) ? 8 : 0) +
  429. ((all_fmt & LIST_BLOCKS) ? 5 : 0);
  430. if (column_width < len)
  431. column_width = len;
  432. }
  433. column_width += tabstops;
  434. ncols = (int) (terminal_width / column_width);
  435. }
  436. if (ncols > 1) {
  437. nrows = nfiles / ncols;
  438. if ((nrows * ncols) < nfiles)
  439. nrows++; /* round up fractionals */
  440. } else {
  441. nrows = nfiles;
  442. ncols = 1;
  443. }
  444. for (row = 0; row < nrows; row++) {
  445. for (nc = 0; nc < ncols; nc++) {
  446. /* reach into the array based on the column and row */
  447. i = (nc * nrows) + row; /* assume display by column */
  448. if (all_fmt & DISP_ROWS)
  449. i = (row * ncols) + nc; /* display across row */
  450. if (i < nfiles) {
  451. if (column > 0) {
  452. nexttab -= column;
  453. while (nexttab--) {
  454. putchar(' ');
  455. column++;
  456. }
  457. }
  458. nexttab = column + column_width;
  459. column += list_single(dn[i]);
  460. }
  461. }
  462. putchar('\n');
  463. column = 0;
  464. }
  465. }
  466. /*----------------------------------------------------------------------*/
  467. static void showdirs(struct dnode **dn, int ndirs, int first)
  468. {
  469. int i, nfiles;
  470. struct dnode **subdnp;
  471. #ifdef CONFIG_FEATURE_LS_RECURSIVE
  472. int dndirs;
  473. struct dnode **dnd;
  474. #endif
  475. if (dn == NULL || ndirs < 1)
  476. return;
  477. for (i = 0; i < ndirs; i++) {
  478. if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
  479. if (!first)
  480. printf("\n");
  481. first = 0;
  482. printf("%s:\n", dn[i]->fullname);
  483. }
  484. subdnp = list_dir(dn[i]->fullname);
  485. nfiles = countfiles(subdnp);
  486. if (nfiles > 0) {
  487. /* list all files at this level */
  488. #ifdef CONFIG_FEATURE_LS_SORTFILES
  489. shellsort(subdnp, nfiles);
  490. #endif
  491. showfiles(subdnp, nfiles);
  492. #ifdef CONFIG_FEATURE_LS_RECURSIVE
  493. if (all_fmt & DISP_RECURSIVE) {
  494. /* recursive- list the sub-dirs */
  495. dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
  496. dndirs = countsubdirs(subdnp, nfiles);
  497. if (dndirs > 0) {
  498. #ifdef CONFIG_FEATURE_LS_SORTFILES
  499. shellsort(dnd, dndirs);
  500. #endif
  501. showdirs(dnd, dndirs, 0);
  502. free(dnd); /* free the array of dnode pointers to the dirs */
  503. }
  504. }
  505. dfree(subdnp, nfiles); /* free the dnodes and the fullname mem */
  506. #endif
  507. }
  508. }
  509. }
  510. /*----------------------------------------------------------------------*/
  511. static struct dnode **list_dir(const char *path)
  512. {
  513. struct dnode *dn, *cur, **dnp;
  514. struct dirent *entry;
  515. DIR *dir;
  516. int i, nfiles;
  517. if (path == NULL)
  518. return (NULL);
  519. dn = NULL;
  520. nfiles = 0;
  521. dir = opendir(path);
  522. if (dir == NULL) {
  523. bb_perror_msg("%s", path);
  524. status = EXIT_FAILURE;
  525. return (NULL); /* could not open the dir */
  526. }
  527. while ((entry = readdir(dir)) != NULL) {
  528. char *fullname;
  529. /* are we going to list the file- it may be . or .. or a hidden file */
  530. if (entry->d_name[0] == '.') {
  531. if ((entry->d_name[1] == 0 || (
  532. entry->d_name[1] == '.'
  533. && entry->d_name[2] == 0))
  534. && !(all_fmt & DISP_DOT))
  535. continue;
  536. if (!(all_fmt & DISP_HIDDEN))
  537. continue;
  538. }
  539. fullname = concat_path_file(path, entry->d_name);
  540. cur = my_stat(fullname, strrchr(fullname, '/') + 1);
  541. if (!cur)
  542. continue;
  543. cur->allocated = 1;
  544. cur->next = dn;
  545. dn = cur;
  546. nfiles++;
  547. }
  548. closedir(dir);
  549. /* now that we know how many files there are
  550. ** allocate memory for an array to hold dnode pointers
  551. */
  552. if (dn == NULL)
  553. return (NULL);
  554. dnp = dnalloc(nfiles);
  555. for (i = 0, cur = dn; i < nfiles; i++) {
  556. dnp[i] = cur; /* save pointer to node in array */
  557. cur = cur->next;
  558. }
  559. return (dnp);
  560. }
  561. /*----------------------------------------------------------------------*/
  562. static int list_single(struct dnode *dn)
  563. {
  564. int i, column = 0;
  565. #ifdef CONFIG_FEATURE_LS_USERNAME
  566. char scratch[16];
  567. #endif
  568. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  569. char *filetime;
  570. time_t ttime, age;
  571. #endif
  572. #if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
  573. struct stat info;
  574. char append;
  575. #endif
  576. if (dn->fullname == NULL)
  577. return (0);
  578. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  579. ttime = dn->dstat.st_mtime; /* the default time */
  580. if (all_fmt & TIME_ACCESS)
  581. ttime = dn->dstat.st_atime;
  582. if (all_fmt & TIME_CHANGE)
  583. ttime = dn->dstat.st_ctime;
  584. filetime = ctime(&ttime);
  585. #endif
  586. #ifdef CONFIG_FEATURE_LS_FILETYPES
  587. append = append_char(dn->dstat.st_mode);
  588. #endif
  589. for (i = 0; i <= 31; i++) {
  590. switch (all_fmt & (1 << i)) {
  591. case LIST_INO:
  592. column += printf("%7ld ", (long int) dn->dstat.st_ino);
  593. break;
  594. case LIST_BLOCKS:
  595. #if _FILE_OFFSET_BITS == 64
  596. column += printf("%4lld ", (long long)dn->dstat.st_blocks >> 1);
  597. #else
  598. column += printf("%4ld ", dn->dstat.st_blocks >> 1);
  599. #endif
  600. break;
  601. case LIST_MODEBITS:
  602. column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
  603. break;
  604. case LIST_NLINKS:
  605. column += printf("%4ld ", (long) dn->dstat.st_nlink);
  606. break;
  607. case LIST_ID_NAME:
  608. #ifdef CONFIG_FEATURE_LS_USERNAME
  609. bb_getpwuid(scratch, dn->dstat.st_uid, sizeof(scratch));
  610. printf("%-8.8s ", scratch);
  611. bb_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch));
  612. printf("%-8.8s", scratch);
  613. column += 17;
  614. break;
  615. #endif
  616. case LIST_ID_NUMERIC:
  617. column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
  618. break;
  619. case LIST_SIZE:
  620. case LIST_DEV:
  621. if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
  622. column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),
  623. (int) minor(dn->dstat.st_rdev));
  624. } else {
  625. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  626. if (all_fmt & LS_DISP_HR) {
  627. column += printf("%9s ",
  628. make_human_readable_str(dn->dstat.st_size, 1, 0));
  629. } else
  630. #endif
  631. {
  632. #if _FILE_OFFSET_BITS == 64
  633. column += printf("%9lld ", (long long) dn->dstat.st_size);
  634. #else
  635. column += printf("%9ld ", dn->dstat.st_size);
  636. #endif
  637. }
  638. }
  639. break;
  640. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  641. case LIST_FULLTIME:
  642. printf("%24.24s ", filetime);
  643. column += 25;
  644. break;
  645. case LIST_DATE_TIME:
  646. if ((all_fmt & LIST_FULLTIME) == 0) {
  647. age = time(NULL) - ttime;
  648. printf("%6.6s ", filetime + 4);
  649. if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
  650. /* hh:mm if less than 6 months old */
  651. printf("%5.5s ", filetime + 11);
  652. } else {
  653. printf(" %4.4s ", filetime + 20);
  654. }
  655. column += 13;
  656. }
  657. break;
  658. #endif
  659. #ifdef CONFIG_SELINUX
  660. case LIST_CONTEXT:
  661. {
  662. char context[80];
  663. int len = 0;
  664. if (dn->sid) {
  665. /* I assume sid initilized with NULL */
  666. len = strlen(dn->sid)+1;
  667. safe_strncpy(context, dn->sid, len);
  668. freecon(dn->sid);
  669. }else {
  670. safe_strncpy(context, "unknown", 8);
  671. }
  672. printf("%-32s ", context);
  673. column += MAX(33, len);
  674. }
  675. break;
  676. #endif
  677. case LIST_FILENAME:
  678. #ifdef CONFIG_FEATURE_LS_COLOR
  679. errno = 0;
  680. if (show_color && !lstat(dn->fullname, &info)) {
  681. printf("\033[%d;%dm", bgcolor(info.st_mode),
  682. fgcolor(info.st_mode));
  683. }
  684. #endif
  685. column += printf("%s", dn->name);
  686. #ifdef CONFIG_FEATURE_LS_COLOR
  687. if (show_color) {
  688. printf("\033[0m");
  689. }
  690. #endif
  691. break;
  692. case LIST_SYMLINK:
  693. if (S_ISLNK(dn->dstat.st_mode)) {
  694. char *lpath = xreadlink(dn->fullname);
  695. if (lpath) {
  696. printf(" -> ");
  697. #if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
  698. if (!stat(dn->fullname, &info)) {
  699. append = append_char(info.st_mode);
  700. }
  701. #endif
  702. #ifdef CONFIG_FEATURE_LS_COLOR
  703. if (show_color) {
  704. errno = 0;
  705. printf("\033[%d;%dm", bgcolor(info.st_mode),
  706. fgcolor(info.st_mode));
  707. }
  708. #endif
  709. column += printf("%s", lpath) + 4;
  710. #ifdef CONFIG_FEATURE_LS_COLOR
  711. if (show_color) {
  712. printf("\033[0m");
  713. }
  714. #endif
  715. free(lpath);
  716. }
  717. }
  718. break;
  719. #ifdef CONFIG_FEATURE_LS_FILETYPES
  720. case LIST_FILETYPE:
  721. if (append != '\0') {
  722. printf("%1c", append);
  723. column++;
  724. }
  725. break;
  726. #endif
  727. }
  728. }
  729. return column;
  730. }
  731. /*----------------------------------------------------------------------*/
  732. /* "[-]Cadil1", POSIX mandated options, busybox always supports */
  733. /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
  734. /* "[-]Ak" GNU options, busybox always supports */
  735. /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
  736. /* "[-]p", POSIX non-mandated options, busybox optionally supports */
  737. /* "[-]SXvThw", GNU options, busybox optionally supports */
  738. /* "[-]K", SELinux mandated options, busybox optionally supports */
  739. /* "[-]e", I think we made this one up */
  740. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  741. # define LS_STR_TIMESTAMPS "cetu"
  742. #else
  743. # define LS_STR_TIMESTAMPS ""
  744. #endif
  745. #ifdef CONFIG_FEATURE_LS_SORTFILES
  746. # define LS_STR_SORTFILES "SXrv"
  747. #else
  748. # define LS_STR_SORTFILES ""
  749. #endif
  750. #ifdef CONFIG_FEATURE_LS_FILETYPES
  751. # define LS_STR_FILETYPES "Fp"
  752. #else
  753. # define LS_STR_FILETYPES ""
  754. #endif
  755. #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
  756. # define LS_STR_FOLLOW_LINKS "L"
  757. #else
  758. # define LS_STR_FOLLOW_LINKS ""
  759. #endif
  760. #ifdef CONFIG_FEATURE_LS_RECURSIVE
  761. # define LS_STR_RECURSIVE "R"
  762. #else
  763. # define LS_STR_RECURSIVE ""
  764. #endif
  765. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  766. # define LS_STR_HUMAN_READABLE "h"
  767. #else
  768. # define LS_STR_HUMAN_READABLE ""
  769. #endif
  770. #ifdef CONFIG_SELINUX
  771. # define LS_STR_SELINUX "K"
  772. #else
  773. # define LS_STR_SELINUX ""
  774. #endif
  775. #ifdef CONFIG_FEATURE_AUTOWIDTH
  776. # define LS_STR_AUTOWIDTH "T:w:"
  777. #else
  778. # define LS_STR_AUTOWIDTH ""
  779. #endif
  780. static const char ls_options[]="Cadil1gnsxAk" \
  781. LS_STR_TIMESTAMPS \
  782. LS_STR_SORTFILES \
  783. LS_STR_FILETYPES \
  784. LS_STR_FOLLOW_LINKS \
  785. LS_STR_RECURSIVE \
  786. LS_STR_HUMAN_READABLE \
  787. LS_STR_SELINUX \
  788. LS_STR_AUTOWIDTH;
  789. #define LIST_MASK_TRIGGER 0
  790. #define STYLE_MASK_TRIGGER STYLE_MASK
  791. #define SORT_MASK_TRIGGER SORT_MASK
  792. #define DISP_MASK_TRIGGER DISP_ROWS
  793. #define TIME_MASK_TRIGGER TIME_MASK
  794. static const unsigned opt_flags[] = {
  795. LIST_SHORT | STYLE_COLUMNS, /* C */
  796. DISP_HIDDEN | DISP_DOT, /* a */
  797. DISP_NOLIST, /* d */
  798. LIST_INO, /* i */
  799. LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
  800. LIST_SHORT | STYLE_SINGLE, /* 1 */
  801. 0, /* g - ingored */
  802. LIST_ID_NUMERIC, /* n */
  803. LIST_BLOCKS, /* s */
  804. DISP_ROWS, /* x */
  805. DISP_HIDDEN, /* A */
  806. #ifdef CONFIG_SELINUX
  807. LIST_CONTEXT, /* k */
  808. #else
  809. 0, /* k - ingored */
  810. #endif
  811. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  812. # ifdef CONFIG_FEATURE_LS_SORTFILES
  813. TIME_CHANGE | SORT_CTIME, /* c */
  814. # else
  815. TIME_CHANGE, /* c */
  816. # endif
  817. LIST_FULLTIME, /* e */
  818. # ifdef CONFIG_FEATURE_LS_SORTFILES
  819. SORT_MTIME, /* t */
  820. # else
  821. 0, /* t - ignored -- is this correct? */
  822. # endif
  823. # ifdef CONFIG_FEATURE_LS_SORTFILES
  824. TIME_ACCESS | SORT_ATIME, /* u */
  825. # else
  826. TIME_ACCESS, /* u */
  827. # endif
  828. #endif
  829. #ifdef CONFIG_FEATURE_LS_SORTFILES
  830. SORT_SIZE, /* S */
  831. SORT_EXT, /* X */
  832. SORT_ORDER_REVERSE, /* r */
  833. SORT_VERSION, /* v */
  834. #endif
  835. #ifdef CONFIG_FEATURE_LS_FILETYPES
  836. LIST_FILETYPE | LIST_EXEC, /* F */
  837. LIST_FILETYPE, /* p */
  838. #endif
  839. #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
  840. FOLLOW_LINKS, /* L */
  841. #endif
  842. #ifdef CONFIG_FEATURE_LS_RECURSIVE
  843. DISP_RECURSIVE, /* R */
  844. #endif
  845. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  846. LS_DISP_HR, /* h */
  847. #endif
  848. #ifdef CONFIG_SELINUX
  849. LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
  850. #endif
  851. #ifdef CONFIG_FEATURE_AUTOWIDTH
  852. 0, 0, /* T, w - ignored */
  853. #endif
  854. (1U<<31)
  855. };
  856. /*----------------------------------------------------------------------*/
  857. int ls_main(int argc, char **argv)
  858. {
  859. struct dnode **dnd;
  860. struct dnode **dnf;
  861. struct dnode **dnp;
  862. struct dnode *dn;
  863. struct dnode *cur;
  864. long opt;
  865. int nfiles = 0;
  866. int dnfiles;
  867. int dndirs;
  868. int oi;
  869. int ac;
  870. int i;
  871. char **av;
  872. #ifdef CONFIG_FEATURE_AUTOWIDTH
  873. char *tabstops_str = NULL;
  874. char *terminal_width_str = NULL;
  875. #endif
  876. #ifdef CONFIG_FEATURE_LS_COLOR
  877. char *color_opt;
  878. #endif
  879. all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO
  880. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  881. | TIME_MOD
  882. #endif
  883. #ifdef CONFIG_FEATURE_LS_SORTFILES
  884. | SORT_NAME | SORT_ORDER_FORWARD
  885. #endif
  886. ;
  887. #ifdef CONFIG_FEATURE_AUTOWIDTH
  888. /* Obtain the terminal width. */
  889. get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
  890. /* Go one less... */
  891. terminal_width--;
  892. #endif
  893. #ifdef CONFIG_FEATURE_LS_COLOR
  894. bb_applet_long_options = ls_color_opt;
  895. #endif
  896. /* process options */
  897. #ifdef CONFIG_FEATURE_AUTOWIDTH
  898. opt = bb_getopt_ulflags(argc, argv, ls_options, &tabstops_str, &terminal_width_str
  899. #ifdef CONFIG_FEATURE_LS_COLOR
  900. , &color_opt
  901. #endif
  902. );
  903. if (tabstops_str) {
  904. tabstops = atoi(tabstops_str);
  905. }
  906. if (terminal_width_str) {
  907. terminal_width = atoi(terminal_width_str);
  908. }
  909. #else
  910. opt = bb_getopt_ulflags(argc, argv, ls_options
  911. #ifdef CONFIG_FEATURE_LS_COLOR
  912. , &color_opt
  913. #endif
  914. );
  915. #endif
  916. for (i = 0; opt_flags[i] != (1U<<31); i++) {
  917. if (opt & (1 << i)) {
  918. unsigned int flags = opt_flags[i];
  919. if (flags & LIST_MASK_TRIGGER) {
  920. all_fmt &= ~LIST_MASK;
  921. }
  922. if (flags & STYLE_MASK_TRIGGER) {
  923. all_fmt &= ~STYLE_MASK;
  924. }
  925. #ifdef CONFIG_FEATURE_LS_SORTFILES
  926. if (flags & SORT_MASK_TRIGGER) {
  927. all_fmt &= ~SORT_MASK;
  928. }
  929. #endif
  930. if (flags & DISP_MASK_TRIGGER) {
  931. all_fmt &= ~DISP_MASK;
  932. }
  933. #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
  934. if (flags & TIME_MASK_TRIGGER) {
  935. all_fmt &= ~TIME_MASK;
  936. }
  937. #endif
  938. if (flags & LIST_CONTEXT) {
  939. all_fmt |= STYLE_SINGLE;
  940. }
  941. #ifdef CONFIG_FEATURE_HUMAN_READABLE
  942. if (opt == 'l') {
  943. all_fmt &= ~LS_DISP_HR;
  944. }
  945. #endif
  946. all_fmt |= flags;
  947. }
  948. }
  949. #ifdef CONFIG_FEATURE_LS_COLOR
  950. {
  951. /* find color bit value - last position for short getopt */
  952. #if CONFIG_FEATURE_LS_COLOR_IS_DEFAULT
  953. char *p;
  954. if ((p = getenv ("LS_COLORS")) != NULL &&
  955. (*p == '\0' || (strcmp(p, "none") == 0))) {
  956. ;
  957. } else if (isatty(STDOUT_FILENO)) {
  958. show_color = 1;
  959. }
  960. #endif
  961. if((opt & (1 << i))) { /* next flag after short options */
  962. if (color_opt == NULL || strcmp("always", color_opt) == 0)
  963. show_color = 1;
  964. else if (color_opt != NULL && strcmp("never", color_opt) == 0)
  965. show_color = 0;
  966. else if (color_opt != NULL && strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
  967. show_color = 1;
  968. }
  969. }
  970. #endif
  971. /* sort out which command line options take precedence */
  972. #ifdef CONFIG_FEATURE_LS_RECURSIVE
  973. if (all_fmt & DISP_NOLIST)
  974. all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
  975. #endif
  976. #if defined (CONFIG_FEATURE_LS_TIMESTAMPS) && defined (CONFIG_FEATURE_LS_SORTFILES)
  977. if (all_fmt & TIME_CHANGE)
  978. all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
  979. if (all_fmt & TIME_ACCESS)
  980. all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
  981. #endif
  982. if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
  983. all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
  984. #ifdef CONFIG_FEATURE_LS_USERNAME
  985. if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
  986. all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
  987. #endif
  988. /* choose a display format */
  989. if ((all_fmt & STYLE_MASK) == STYLE_AUTO)
  990. #if STYLE_AUTO != 0
  991. all_fmt = (all_fmt & ~STYLE_MASK)
  992. | (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
  993. #else
  994. all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
  995. #endif
  996. /*
  997. * when there are no cmd line args we have to supply a default "." arg.
  998. * we will create a second argv array, "av" that will hold either
  999. * our created "." arg, or the real cmd line args. The av array
  1000. * just holds the pointers- we don't move the date the pointers
  1001. * point to.
  1002. */
  1003. ac = argc - optind; /* how many cmd line args are left */
  1004. if (ac < 1) {
  1005. static const char * const dotdir[] = { "." };
  1006. av = (char **) dotdir;
  1007. ac = 1;
  1008. } else {
  1009. av = argv + optind;
  1010. }
  1011. /* now, everything is in the av array */
  1012. if (ac > 1)
  1013. all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
  1014. /* stuff the command line file names into an dnode array */
  1015. dn = NULL;
  1016. for (oi = 0; oi < ac; oi++) {
  1017. cur = my_stat(av[oi], av[oi]);
  1018. if (!cur)
  1019. continue;
  1020. cur->allocated = 0;
  1021. cur->next = dn;
  1022. dn = cur;
  1023. nfiles++;
  1024. }
  1025. /* now that we know how many files there are
  1026. ** allocate memory for an array to hold dnode pointers
  1027. */
  1028. dnp = dnalloc(nfiles);
  1029. for (i = 0, cur = dn; i < nfiles; i++) {
  1030. dnp[i] = cur; /* save pointer to node in array */
  1031. cur = cur->next;
  1032. }
  1033. if (all_fmt & DISP_NOLIST) {
  1034. #ifdef CONFIG_FEATURE_LS_SORTFILES
  1035. shellsort(dnp, nfiles);
  1036. #endif
  1037. if (nfiles > 0)
  1038. showfiles(dnp, nfiles);
  1039. } else {
  1040. dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
  1041. dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
  1042. dndirs = countdirs(dnp, nfiles);
  1043. dnfiles = nfiles - dndirs;
  1044. if (dnfiles > 0) {
  1045. #ifdef CONFIG_FEATURE_LS_SORTFILES
  1046. shellsort(dnf, dnfiles);
  1047. #endif
  1048. showfiles(dnf, dnfiles);
  1049. if (ENABLE_FEATURE_CLEAN_UP)
  1050. free(dnf);
  1051. }
  1052. if (dndirs > 0) {
  1053. #ifdef CONFIG_FEATURE_LS_SORTFILES
  1054. shellsort(dnd, dndirs);
  1055. #endif
  1056. showdirs(dnd, dndirs, dnfiles == 0);
  1057. if (ENABLE_FEATURE_CLEAN_UP)
  1058. free(dnd);
  1059. }
  1060. }
  1061. if (ENABLE_FEATURE_CLEAN_UP)
  1062. dfree(dnp, nfiles);
  1063. return (status);
  1064. }