3
0

ls.c 29 KB

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