ls.c 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
  4. *
  5. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  6. */
  7. /* [date unknown. Perhaps before year 2000]
  8. * To achieve a small memory footprint, this version of 'ls' doesn't do any
  9. * file sorting, and only has the most essential command line switches
  10. * (i.e., the ones I couldn't live without :-) All features which involve
  11. * linking in substantial chunks of libc can be disabled.
  12. *
  13. * Although I don't really want to add new features to this program to
  14. * keep it small, I *am* interested to receive bug fixes and ways to make
  15. * it more portable.
  16. *
  17. * KNOWN BUGS:
  18. * 1. hidden files can make column width too large
  19. *
  20. * NON-OPTIMAL BEHAVIOUR:
  21. * 1. autowidth reads directories twice
  22. * 2. if you do a short directory listing without filetype characters
  23. * appended, there's no need to stat each one
  24. * PORTABILITY:
  25. * 1. requires lstat (BSD) - how do you do it without?
  26. *
  27. * [2009-03]
  28. * ls sorts listing now, and supports almost all options.
  29. */
  30. //config:config LS
  31. //config: bool "ls (14 kb)"
  32. //config: default y
  33. //config: help
  34. //config: ls is used to list the contents of directories.
  35. //config:
  36. //config:config FEATURE_LS_FILETYPES
  37. //config: bool "Enable filetyping options (-p and -F)"
  38. //config: default y
  39. //config: depends on LS
  40. //config:
  41. //config:config FEATURE_LS_FOLLOWLINKS
  42. //config: bool "Enable symlinks dereferencing (-L)"
  43. //config: default y
  44. //config: depends on LS
  45. //config:
  46. //config:config FEATURE_LS_RECURSIVE
  47. //config: bool "Enable recursion (-R)"
  48. //config: default y
  49. //config: depends on LS
  50. //config:
  51. //config:config FEATURE_LS_WIDTH
  52. //config: bool "Enable -w WIDTH and window size autodetection"
  53. //config: default y
  54. //config: depends on LS
  55. //config:
  56. //config:config FEATURE_LS_SORTFILES
  57. //config: bool "Sort the file names"
  58. //config: default y
  59. //config: depends on LS
  60. //config: help
  61. //config: Allow ls to sort file names alphabetically.
  62. //config:
  63. //config:config FEATURE_LS_TIMESTAMPS
  64. //config: bool "Show file timestamps"
  65. //config: default y
  66. //config: depends on LS
  67. //config: help
  68. //config: Allow ls to display timestamps for files.
  69. //config:
  70. //config:config FEATURE_LS_USERNAME
  71. //config: bool "Show username/groupnames"
  72. //config: default y
  73. //config: depends on LS
  74. //config: help
  75. //config: Allow ls to display username/groupname for files.
  76. //config:
  77. //config:config FEATURE_LS_COLOR
  78. //config: bool "Allow use of color to identify file types"
  79. //config: default y
  80. //config: depends on LS && LONG_OPTS
  81. //config: help
  82. //config: This enables the --color option to ls.
  83. //config:
  84. //config:config FEATURE_LS_COLOR_IS_DEFAULT
  85. //config: bool "Produce colored ls output by default"
  86. //config: default y
  87. //config: depends on FEATURE_LS_COLOR
  88. //config: help
  89. //config: Saying yes here will turn coloring on by default,
  90. //config: even if no "--color" option is given to the ls command.
  91. //config: This is not recommended, since the colors are not
  92. //config: configurable, and the output may not be legible on
  93. //config: many output screens.
  94. //applet:IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
  95. //kbuild:lib-$(CONFIG_LS) += ls.o
  96. //usage:#define ls_trivial_usage
  97. //usage: "[-1AaCxd"
  98. //usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
  99. //usage: IF_FEATURE_LS_RECURSIVE("R")
  100. //usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
  101. //usage: IF_FEATURE_HUMAN_READABLE("h")
  102. //usage: IF_FEATURE_LS_SORTFILES("rSXv")
  103. //usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
  104. //usage: IF_SELINUX("kZ") "]"
  105. //usage: IF_FEATURE_LS_WIDTH(" [-w WIDTH]") " [FILE]..."
  106. //usage:#define ls_full_usage "\n\n"
  107. //usage: "List directory contents\n"
  108. //usage: "\n -1 One column output"
  109. //usage: "\n -a Include names starting with ."
  110. //usage: "\n -A Like -a, but exclude . and .."
  111. ////usage: "\n -C List by columns" - don't show, this is a default anyway
  112. //usage: "\n -x List by lines"
  113. //usage: "\n -d List directory names, not contents"
  114. //usage: IF_FEATURE_LS_FOLLOWLINKS(
  115. //usage: "\n -L Follow symlinks"
  116. //usage: "\n -H Follow symlinks on command line"
  117. //usage: )
  118. //usage: IF_FEATURE_LS_RECURSIVE(
  119. //usage: "\n -R Recurse"
  120. //usage: )
  121. //usage: IF_FEATURE_LS_FILETYPES(
  122. //usage: "\n -p Append / to directory names"
  123. //usage: "\n -F Append indicator (one of */=@|) to names"
  124. //usage: )
  125. //usage: "\n -l Long format"
  126. //usage: "\n -i List inode numbers"
  127. //usage: "\n -n List numeric UIDs and GIDs instead of names"
  128. //usage: "\n -s List allocated blocks"
  129. //usage: IF_FEATURE_LS_TIMESTAMPS(
  130. //usage: "\n -lc List ctime"
  131. //usage: "\n -lu List atime"
  132. //usage: )
  133. //usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(
  134. //usage: "\n --full-time List full date/time"
  135. //usage: ))
  136. //usage: IF_FEATURE_HUMAN_READABLE(
  137. //usage: "\n -h Human readable sizes (1K 243M 2G)"
  138. //usage: )
  139. //usage: IF_FEATURE_LS_SORTFILES(
  140. //usage: IF_LONG_OPTS(
  141. //usage: "\n --group-directories-first"
  142. //usage: )
  143. //usage: "\n -S Sort by size"
  144. //usage: "\n -X Sort by extension"
  145. //usage: "\n -v Sort by version"
  146. //usage: )
  147. //usage: IF_FEATURE_LS_TIMESTAMPS(
  148. //usage: "\n -t Sort by mtime"
  149. //usage: "\n -tc Sort by ctime"
  150. //usage: "\n -tu Sort by atime"
  151. //usage: )
  152. //usage: "\n -r Reverse sort order"
  153. //usage: IF_SELINUX(
  154. //usage: "\n -Z List security context and permission"
  155. //usage: )
  156. //usage: IF_FEATURE_LS_WIDTH(
  157. //usage: "\n -w N Format N columns wide"
  158. //usage: )
  159. //usage: IF_FEATURE_LS_COLOR(
  160. //usage: "\n --color[={always,never,auto}]"
  161. //usage: )
  162. #include "libbb.h"
  163. #include "common_bufsiz.h"
  164. #include "unicode.h"
  165. /* This is a NOEXEC applet. Be very careful! */
  166. #if ENABLE_FTPD
  167. /* ftpd uses ls, and without timestamps Mozilla won't understand
  168. * ftpd's LIST output.
  169. */
  170. # undef CONFIG_FEATURE_LS_TIMESTAMPS
  171. # undef ENABLE_FEATURE_LS_TIMESTAMPS
  172. # undef IF_FEATURE_LS_TIMESTAMPS
  173. # undef IF_NOT_FEATURE_LS_TIMESTAMPS
  174. # define CONFIG_FEATURE_LS_TIMESTAMPS 1
  175. # define ENABLE_FEATURE_LS_TIMESTAMPS 1
  176. # define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
  177. # define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
  178. #endif
  179. enum {
  180. TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
  181. SPLIT_FILE = 0,
  182. SPLIT_DIR = 1,
  183. SPLIT_SUBDIR = 2,
  184. };
  185. /* -Cadi1l Std options, busybox always supports */
  186. /* -gnsxA Std options, busybox always supports */
  187. /* -Q GNU option, busybox always supports */
  188. /* -k Std option, busybox always supports (by ignoring) */
  189. /* It means "for -s, show sizes in kbytes" */
  190. /* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
  191. /* since otherwise -s shows kbytes anyway */
  192. /* -LHRctur Std options, busybox optionally supports */
  193. /* -Fp Std options, busybox optionally supports */
  194. /* -SXvhTw GNU options, busybox optionally supports */
  195. /* -T WIDTH Ignored (we don't use tabs on output) */
  196. /* -Z SELinux mandated option, busybox optionally supports */
  197. #define ls_options \
  198. "Cadi1lgnsxAk" /* 12 opts, total 12 */ \
  199. IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \
  200. IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \
  201. IF_SELINUX("Z") /* 1, 16 */ \
  202. "Q" /* 1, 17 */ \
  203. IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \
  204. IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \
  205. IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \
  206. IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \
  207. IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
  208. enum {
  209. OPT_C = (1 << 0),
  210. OPT_a = (1 << 1),
  211. OPT_d = (1 << 2),
  212. OPT_i = (1 << 3),
  213. OPT_1 = (1 << 4),
  214. OPT_l = (1 << 5),
  215. OPT_g = (1 << 6),
  216. OPT_n = (1 << 7),
  217. OPT_s = (1 << 8),
  218. OPT_x = (1 << 9),
  219. OPT_A = (1 << 10),
  220. //OPT_k = (1 << 11),
  221. OPTBIT_F = 12,
  222. OPTBIT_p, /* 13 */
  223. OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
  224. OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
  225. OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
  226. OPTBIT_c, /* 17 */
  227. OPTBIT_t, /* 18 */
  228. OPTBIT_u, /* 19 */
  229. OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
  230. OPTBIT_X, /* 21 */
  231. OPTBIT_r, /* 22 */
  232. OPTBIT_v, /* 23 */
  233. OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
  234. OPTBIT_H, /* 25 */
  235. OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
  236. OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
  237. OPTBIT_w, /* 28 */
  238. OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
  239. OPTBIT_dirs_first,
  240. OPTBIT_color, /* 31 */
  241. /* with long opts, we use all 32 bits */
  242. OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
  243. OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
  244. OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
  245. OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
  246. OPT_Q = (1 << OPTBIT_Q),
  247. OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
  248. OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
  249. OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
  250. OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
  251. OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
  252. OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
  253. OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
  254. OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
  255. OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
  256. OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
  257. OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH,
  258. OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH,
  259. OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
  260. OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
  261. OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
  262. };
  263. /*
  264. * a directory entry and its stat info
  265. */
  266. struct dnode {
  267. const char *name; /* usually basename, but think "ls -l dir/file" */
  268. const char *fullname; /* full name (usable for stat etc) */
  269. struct dnode *dn_next; /* for linked list */
  270. IF_SELINUX(security_context_t sid;)
  271. smallint fname_allocated;
  272. /* Used to avoid re-doing [l]stat at printout stage
  273. * if we already collected needed data in scan stage:
  274. */
  275. mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
  276. mode_t dn_mode_stat; /* obtained with stat, or 0 */
  277. // struct stat dstat;
  278. // struct stat is huge. We don't need it in full.
  279. // At least we don't need st_dev and st_blksize,
  280. // but there are invisible fields as well
  281. // (such as nanosecond-resolution timespamps)
  282. // and padding, which we also don't want to store.
  283. // We also pre-parse dev_t dn_rdev (in glibc, it's huge).
  284. // On 32-bit uclibc: dnode size went from 112 to 84 bytes.
  285. //
  286. /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
  287. mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
  288. off_t dn_size;
  289. #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
  290. time_t dn_time;
  291. #endif
  292. ino_t dn_ino;
  293. blkcnt_t dn_blocks;
  294. nlink_t dn_nlink;
  295. uid_t dn_uid;
  296. gid_t dn_gid;
  297. int dn_rdev_maj;
  298. int dn_rdev_min;
  299. // dev_t dn_dev;
  300. // blksize_t dn_blksize;
  301. };
  302. struct globals {
  303. #if ENABLE_FEATURE_LS_COLOR
  304. smallint show_color;
  305. # define G_show_color (G.show_color)
  306. #else
  307. # define G_show_color 0
  308. #endif
  309. smallint exit_code;
  310. smallint show_dirname;
  311. #if ENABLE_FEATURE_LS_WIDTH
  312. unsigned terminal_width;
  313. # define G_terminal_width (G.terminal_width)
  314. #else
  315. # define G_terminal_width TERMINAL_WIDTH
  316. #endif
  317. #if ENABLE_FEATURE_LS_TIMESTAMPS
  318. /* Do time() just once. Saves one syscall per file for "ls -l" */
  319. time_t current_time_t;
  320. #endif
  321. } FIX_ALIASING;
  322. #define G (*(struct globals*)bb_common_bufsiz1)
  323. #define INIT_G() do { \
  324. setup_common_bufsiz(); \
  325. /* we have to zero it out because of NOEXEC */ \
  326. memset(&G, 0, sizeof(G)); \
  327. IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \
  328. IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
  329. } while (0)
  330. #define ESC "\033"
  331. /*** Output code ***/
  332. /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
  333. * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
  334. * 3/7:multiplexed char/block device)
  335. * and we use 0 for unknown and 15 for executables (see below) */
  336. #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
  337. /* un fi chr - dir - blk - file - link - sock - - exe */
  338. #define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
  339. /* 036 black foreground 050 black background
  340. 037 red foreground 051 red background
  341. 040 green foreground 052 green background
  342. 041 brown foreground 053 brown background
  343. 042 blue foreground 054 blue background
  344. 043 magenta (purple) foreground 055 magenta background
  345. 044 cyan (light blue) foreground 056 cyan background
  346. 045 gray foreground 057 white background
  347. */
  348. #define COLOR(mode) ( \
  349. /*un fi chr - dir - blk - file - link - sock - - exe */ \
  350. "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
  351. [TYPEINDEX(mode)])
  352. /* Select normal (0) [actually "reset all"] or bold (1)
  353. * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
  354. * let's use 7 for "impossible" types, just for fun)
  355. * Note: coreutils 6.9 uses inverted red for setuid binaries.
  356. */
  357. #define ATTR(mode) ( \
  358. /*un fi chr - dir - blk - file- link- sock- - exe */ \
  359. "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
  360. [TYPEINDEX(mode)])
  361. #if ENABLE_FEATURE_LS_COLOR
  362. /* mode of zero is interpreted as "unknown" (stat failed) */
  363. static char fgcolor(mode_t mode)
  364. {
  365. if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
  366. return COLOR(0xF000); /* File is executable ... */
  367. return COLOR(mode);
  368. }
  369. static char bold(mode_t mode)
  370. {
  371. if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
  372. return ATTR(0xF000); /* File is executable ... */
  373. return ATTR(mode);
  374. }
  375. #endif
  376. #if ENABLE_FEATURE_LS_FILETYPES
  377. static char append_char(mode_t mode)
  378. {
  379. if (!(option_mask32 & (OPT_F|OPT_p)))
  380. return '\0';
  381. if (S_ISDIR(mode))
  382. return '/';
  383. if (!(option_mask32 & OPT_F))
  384. return '\0';
  385. if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
  386. return '*';
  387. return APPCHAR(mode);
  388. }
  389. #endif
  390. static unsigned calc_name_len(const char *name)
  391. {
  392. unsigned len;
  393. uni_stat_t uni_stat;
  394. // TODO: quote tab as \t, etc, if -Q
  395. name = printable_string2(&uni_stat, name);
  396. if (!(option_mask32 & OPT_Q)) {
  397. return uni_stat.unicode_width;
  398. }
  399. len = 2 + uni_stat.unicode_width;
  400. while (*name) {
  401. if (*name == '"' || *name == '\\') {
  402. len++;
  403. }
  404. name++;
  405. }
  406. return len;
  407. }
  408. /* Return the number of used columns.
  409. * Note that only columnar output uses return value.
  410. * -l and -1 modes don't care.
  411. * coreutils 7.2 also supports:
  412. * ls -b (--escape) = octal escapes (although it doesn't look like working)
  413. * ls -N (--literal) = not escape at all
  414. */
  415. static unsigned print_name(const char *name)
  416. {
  417. unsigned len;
  418. uni_stat_t uni_stat;
  419. // TODO: quote tab as \t, etc, if -Q
  420. name = printable_string2(&uni_stat, name);
  421. if (!(option_mask32 & OPT_Q)) {
  422. fputs_stdout(name);
  423. return uni_stat.unicode_width;
  424. }
  425. len = 2 + uni_stat.unicode_width;
  426. putchar('"');
  427. while (*name) {
  428. if (*name == '"' || *name == '\\') {
  429. putchar('\\');
  430. len++;
  431. }
  432. putchar(*name);
  433. name++;
  434. }
  435. putchar('"');
  436. return len;
  437. }
  438. /* Return the number of used columns.
  439. * Note that only columnar output uses return value,
  440. * -l and -1 modes don't care.
  441. */
  442. static NOINLINE unsigned display_single(const struct dnode *dn)
  443. {
  444. unsigned column = 0;
  445. char *lpath;
  446. int opt;
  447. #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
  448. struct stat statbuf;
  449. #endif
  450. #if ENABLE_FEATURE_LS_FILETYPES
  451. char append = append_char(dn->dn_mode);
  452. #endif
  453. opt = option_mask32;
  454. /* Do readlink early, so that if it fails, error message
  455. * does not appear *inside* the "ls -l" line */
  456. lpath = NULL;
  457. if (opt & OPT_l)
  458. if (S_ISLNK(dn->dn_mode))
  459. lpath = xmalloc_readlink_or_warn(dn->fullname);
  460. if (opt & OPT_i) /* show inode# */
  461. column += printf("%7llu ", (long long) dn->dn_ino);
  462. //TODO: -h should affect -s too:
  463. if (opt & OPT_s) /* show allocated blocks */
  464. column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
  465. if (opt & OPT_l) {
  466. /* long listing: show mode */
  467. char modestr[12];
  468. column += printf("%-10s ", bb_mode_string(modestr, dn->dn_mode));
  469. /* long listing: show number of links */
  470. column += printf("%4lu ", (long) dn->dn_nlink);
  471. /* long listing: show user/group */
  472. if (opt & OPT_n) {
  473. if (opt & OPT_g)
  474. column += printf("%-8u ", (int) dn->dn_gid);
  475. else
  476. column += printf("%-8u %-8u ",
  477. (int) dn->dn_uid,
  478. (int) dn->dn_gid);
  479. }
  480. #if ENABLE_FEATURE_LS_USERNAME
  481. else {
  482. if (opt & OPT_g) {
  483. column += printf("%-8.8s ",
  484. get_cached_groupname(dn->dn_gid));
  485. } else {
  486. column += printf("%-8.8s %-8.8s ",
  487. get_cached_username(dn->dn_uid),
  488. get_cached_groupname(dn->dn_gid));
  489. }
  490. }
  491. #endif
  492. #if ENABLE_SELINUX
  493. }
  494. if (opt & OPT_Z) {
  495. column += printf("%-32s ", dn->sid ? dn->sid : "?");
  496. freecon(dn->sid);
  497. }
  498. if (opt & OPT_l) {
  499. #endif
  500. /* long listing: show size */
  501. if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
  502. column += printf("%4u, %3u ",
  503. dn->dn_rdev_maj,
  504. dn->dn_rdev_min);
  505. } else {
  506. if (opt & OPT_h) {
  507. column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
  508. /* print size, show one fractional, use suffixes */
  509. make_human_readable_str(dn->dn_size, 1, 0)
  510. );
  511. } else {
  512. column += printf("%9"OFF_FMT"u ", dn->dn_size);
  513. }
  514. }
  515. #if ENABLE_FEATURE_LS_TIMESTAMPS
  516. /* long listing: show {m,c,a}time */
  517. if (opt & OPT_full_time) { /* --full-time */
  518. /* coreutils 8.4 ls --full-time prints:
  519. * 2009-07-13 17:49:27.000000000 +0200
  520. * we don't show fractional seconds.
  521. */
  522. char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
  523. strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
  524. localtime(&dn->dn_time));
  525. column += printf("%s ", buf);
  526. } else { /* ordinary time format */
  527. /* G.current_time_t is ~== time(NULL) */
  528. char *filetime = ctime(&dn->dn_time);
  529. /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
  530. time_t age = G.current_time_t - dn->dn_time;
  531. if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
  532. /* less than 6 months old */
  533. /* "mmm dd hh:mm " */
  534. printf("%.12s ", filetime + 4);
  535. } else {
  536. /* "mmm dd yyyy " */
  537. /* "mmm dd yyyyy " after year 9999 :) */
  538. strchr(filetime + 20, '\n')[0] = ' ';
  539. printf("%.7s%6s", filetime + 4, filetime + 20);
  540. }
  541. column += 13;
  542. }
  543. #endif
  544. }
  545. #if ENABLE_FEATURE_LS_COLOR
  546. if (G_show_color) {
  547. mode_t mode = dn->dn_mode_lstat;
  548. if (!mode)
  549. if (lstat(dn->fullname, &statbuf) == 0)
  550. mode = statbuf.st_mode;
  551. printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
  552. }
  553. #endif
  554. column += print_name(dn->name);
  555. if (G_show_color) {
  556. printf(ESC"[m");
  557. }
  558. if (lpath) {
  559. printf(" -> ");
  560. #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
  561. if ((opt & (OPT_F|OPT_p))
  562. || G_show_color
  563. ) {
  564. mode_t mode = dn->dn_mode_stat;
  565. if (!mode)
  566. if (stat(dn->fullname, &statbuf) == 0)
  567. mode = statbuf.st_mode;
  568. # if ENABLE_FEATURE_LS_FILETYPES
  569. append = append_char(mode);
  570. # endif
  571. # if ENABLE_FEATURE_LS_COLOR
  572. if (G_show_color) {
  573. printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
  574. }
  575. # endif
  576. }
  577. #endif
  578. column += print_name(lpath) + 4;
  579. free(lpath);
  580. if (G_show_color) {
  581. printf(ESC"[m");
  582. }
  583. }
  584. #if ENABLE_FEATURE_LS_FILETYPES
  585. if (opt & (OPT_F|OPT_p)) {
  586. if (append) {
  587. putchar(append);
  588. column++;
  589. }
  590. }
  591. #endif
  592. return column;
  593. }
  594. static void display_files(struct dnode **dn, unsigned nfiles)
  595. {
  596. unsigned i, ncols, nrows, row, nc;
  597. unsigned column;
  598. unsigned nexttab;
  599. unsigned column_width = 0; /* used only by coulmnal output */
  600. if (option_mask32 & (OPT_l|OPT_1)) {
  601. ncols = 1;
  602. } else {
  603. /* find the longest file name, use that as the column width */
  604. for (i = 0; dn[i]; i++) {
  605. int len = calc_name_len(dn[i]->name);
  606. if (column_width < len)
  607. column_width = len;
  608. }
  609. column_width += 2
  610. + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
  611. + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
  612. + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
  613. ;
  614. ncols = (unsigned)G_terminal_width / column_width;
  615. }
  616. if (ncols > 1) {
  617. nrows = nfiles / ncols;
  618. if (nrows * ncols < nfiles)
  619. nrows++; /* round up fractionals */
  620. } else {
  621. nrows = nfiles;
  622. ncols = 1;
  623. }
  624. column = 0;
  625. nexttab = 0;
  626. for (row = 0; row < nrows; row++) {
  627. for (nc = 0; nc < ncols; nc++) {
  628. /* reach into the array based on the column and row */
  629. if (option_mask32 & OPT_x)
  630. i = (row * ncols) + nc; /* display across row */
  631. else
  632. i = (nc * nrows) + row; /* display by column */
  633. if (i < nfiles) {
  634. if (column > 0) {
  635. nexttab -= column;
  636. printf("%*s", nexttab, "");
  637. column += nexttab;
  638. }
  639. nexttab = column + column_width;
  640. column += display_single(dn[i]);
  641. }
  642. }
  643. putchar('\n');
  644. column = 0;
  645. }
  646. }
  647. /*** Dir scanning code ***/
  648. static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
  649. {
  650. struct stat statbuf;
  651. struct dnode *cur;
  652. cur = xzalloc(sizeof(*cur));
  653. cur->fullname = fullname;
  654. cur->name = name;
  655. if ((option_mask32 & OPT_L) || force_follow) {
  656. #if ENABLE_SELINUX
  657. if (option_mask32 & OPT_Z) {
  658. getfilecon(fullname, &cur->sid);
  659. }
  660. #endif
  661. if (stat(fullname, &statbuf)) {
  662. bb_simple_perror_msg(fullname);
  663. G.exit_code = EXIT_FAILURE;
  664. free(cur);
  665. return NULL;
  666. }
  667. cur->dn_mode_stat = statbuf.st_mode;
  668. } else {
  669. #if ENABLE_SELINUX
  670. if (option_mask32 & OPT_Z) {
  671. lgetfilecon(fullname, &cur->sid);
  672. }
  673. #endif
  674. if (lstat(fullname, &statbuf)) {
  675. bb_simple_perror_msg(fullname);
  676. G.exit_code = EXIT_FAILURE;
  677. free(cur);
  678. return NULL;
  679. }
  680. cur->dn_mode_lstat = statbuf.st_mode;
  681. }
  682. /* cur->dstat = statbuf: */
  683. cur->dn_mode = statbuf.st_mode ;
  684. cur->dn_size = statbuf.st_size ;
  685. #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
  686. cur->dn_time = statbuf.st_mtime ;
  687. if (option_mask32 & OPT_u)
  688. cur->dn_time = statbuf.st_atime;
  689. if (option_mask32 & OPT_c)
  690. cur->dn_time = statbuf.st_ctime;
  691. #endif
  692. cur->dn_ino = statbuf.st_ino ;
  693. cur->dn_blocks = statbuf.st_blocks;
  694. cur->dn_nlink = statbuf.st_nlink ;
  695. cur->dn_uid = statbuf.st_uid ;
  696. cur->dn_gid = statbuf.st_gid ;
  697. cur->dn_rdev_maj = major(statbuf.st_rdev);
  698. cur->dn_rdev_min = minor(statbuf.st_rdev);
  699. return cur;
  700. }
  701. static unsigned count_dirs(struct dnode **dn, int which)
  702. {
  703. unsigned dirs, all;
  704. if (!dn)
  705. return 0;
  706. dirs = all = 0;
  707. for (; *dn; dn++) {
  708. const char *name;
  709. all++;
  710. if (!S_ISDIR((*dn)->dn_mode))
  711. continue;
  712. name = (*dn)->name;
  713. if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
  714. /* or if it's not . or .. */
  715. || name[0] != '.'
  716. || (name[1] && (name[1] != '.' || name[2]))
  717. ) {
  718. dirs++;
  719. }
  720. }
  721. return which != SPLIT_FILE ? dirs : all - dirs;
  722. }
  723. /* get memory to hold an array of pointers */
  724. static struct dnode **dnalloc(unsigned num)
  725. {
  726. if (num < 1)
  727. return NULL;
  728. num++; /* so that we have terminating NULL */
  729. return xzalloc(num * sizeof(struct dnode *));
  730. }
  731. #if ENABLE_FEATURE_LS_RECURSIVE
  732. static void dfree(struct dnode **dnp)
  733. {
  734. unsigned i;
  735. if (dnp == NULL)
  736. return;
  737. for (i = 0; dnp[i]; i++) {
  738. struct dnode *cur = dnp[i];
  739. if (cur->fname_allocated)
  740. free((char*)cur->fullname);
  741. free(cur);
  742. }
  743. free(dnp);
  744. }
  745. #else
  746. #define dfree(...) ((void)0)
  747. #endif
  748. /* Returns NULL-terminated malloced vector of pointers (or NULL) */
  749. static struct dnode **splitdnarray(struct dnode **dn, int which)
  750. {
  751. unsigned dncnt, d;
  752. struct dnode **dnp;
  753. if (dn == NULL)
  754. return NULL;
  755. /* count how many dirs or files there are */
  756. dncnt = count_dirs(dn, which);
  757. /* allocate a file array and a dir array */
  758. dnp = dnalloc(dncnt);
  759. /* copy the entrys into the file or dir array */
  760. for (d = 0; *dn; dn++) {
  761. if (S_ISDIR((*dn)->dn_mode)) {
  762. const char *name;
  763. if (which == SPLIT_FILE)
  764. continue;
  765. name = (*dn)->name;
  766. if ((which & SPLIT_DIR) /* any dir... */
  767. /* ... or not . or .. */
  768. || name[0] != '.'
  769. || (name[1] && (name[1] != '.' || name[2]))
  770. ) {
  771. dnp[d++] = *dn;
  772. }
  773. } else
  774. if (which == SPLIT_FILE) {
  775. dnp[d++] = *dn;
  776. }
  777. }
  778. return dnp;
  779. }
  780. #if ENABLE_FEATURE_LS_SORTFILES
  781. static int sortcmp(const void *a, const void *b)
  782. {
  783. struct dnode *d1 = *(struct dnode **)a;
  784. struct dnode *d2 = *(struct dnode **)b;
  785. unsigned opt = option_mask32;
  786. off_t dif;
  787. dif = 0; /* assume sort by name */
  788. // TODO: use pre-initialized function pointer
  789. // instead of branch forest
  790. if (opt & OPT_dirs_first) {
  791. dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
  792. if (dif != 0)
  793. goto maybe_invert_and_ret;
  794. }
  795. if (opt & OPT_S) { /* sort by size */
  796. dif = (d2->dn_size - d1->dn_size);
  797. } else
  798. if (opt & OPT_t) { /* sort by time */
  799. dif = (d2->dn_time - d1->dn_time);
  800. } else
  801. #if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
  802. if (opt & OPT_v) { /* sort by version */
  803. dif = strverscmp(d1->name, d2->name);
  804. } else
  805. #endif
  806. if (opt & OPT_X) { /* sort by extension */
  807. dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
  808. }
  809. if (dif == 0) {
  810. /* sort by name, use as tie breaker for other sorts */
  811. if (ENABLE_LOCALE_SUPPORT)
  812. dif = strcoll(d1->name, d2->name);
  813. else
  814. dif = strcmp(d1->name, d2->name);
  815. } else {
  816. /* Make dif fit into an int */
  817. if (sizeof(dif) > sizeof(int)) {
  818. enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
  819. /* shift leaving only "int" worth of bits */
  820. /* (this requires dif != 0, and here it is nonzero) */
  821. dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
  822. }
  823. }
  824. maybe_invert_and_ret:
  825. return (opt & OPT_r) ? -(int)dif : (int)dif;
  826. }
  827. static void dnsort(struct dnode **dn, int size)
  828. {
  829. qsort(dn, size, sizeof(*dn), sortcmp);
  830. }
  831. static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
  832. {
  833. dnsort(dn, nfiles);
  834. display_files(dn, nfiles);
  835. }
  836. #else
  837. # define dnsort(dn, size) ((void)0)
  838. # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
  839. #endif
  840. /* Returns NULL-terminated malloced vector of pointers (or NULL) */
  841. static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
  842. {
  843. struct dnode *dn, *cur, **dnp;
  844. struct dirent *entry;
  845. DIR *dir;
  846. unsigned i, nfiles;
  847. *nfiles_p = 0;
  848. dir = warn_opendir(path);
  849. if (dir == NULL) {
  850. G.exit_code = EXIT_FAILURE;
  851. return NULL; /* could not open the dir */
  852. }
  853. dn = NULL;
  854. nfiles = 0;
  855. while ((entry = readdir(dir)) != NULL) {
  856. char *fullname;
  857. /* are we going to list the file- it may be . or .. or a hidden file */
  858. if (entry->d_name[0] == '.') {
  859. if (!(option_mask32 & (OPT_a|OPT_A)))
  860. continue; /* skip all dotfiles if no -a/-A */
  861. if (!(option_mask32 & OPT_a)
  862. && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
  863. ) {
  864. continue; /* if only -A, skip . and .. but show other dotfiles */
  865. }
  866. }
  867. fullname = concat_path_file(path, entry->d_name);
  868. cur = my_stat(fullname, bb_basename(fullname), 0);
  869. if (!cur) {
  870. free(fullname);
  871. continue;
  872. }
  873. cur->fname_allocated = 1;
  874. cur->dn_next = dn;
  875. dn = cur;
  876. nfiles++;
  877. }
  878. closedir(dir);
  879. if (dn == NULL)
  880. return NULL;
  881. /* now that we know how many files there are
  882. * allocate memory for an array to hold dnode pointers
  883. */
  884. *nfiles_p = nfiles;
  885. dnp = dnalloc(nfiles);
  886. for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
  887. dnp[i] = dn; /* save pointer to node in array */
  888. dn = dn->dn_next;
  889. if (!dn)
  890. break;
  891. }
  892. return dnp;
  893. }
  894. #if ENABLE_DESKTOP
  895. /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
  896. * If any of the -l, -n, -s options is specified, each list
  897. * of files within the directory shall be preceded by a
  898. * status line indicating the number of file system blocks
  899. * occupied by files in the directory in 512-byte units if
  900. * the -k option is not specified, or 1024-byte units if the
  901. * -k option is specified, rounded up to the next integral
  902. * number of units.
  903. */
  904. /* by Jorgen Overgaard (jorgen AT antistaten.se) */
  905. static off_t calculate_blocks(struct dnode **dn)
  906. {
  907. uoff_t blocks = 1;
  908. if (dn) {
  909. while (*dn) {
  910. /* st_blocks is in 512 byte blocks */
  911. blocks += (*dn)->dn_blocks;
  912. dn++;
  913. }
  914. }
  915. /* Even though standard says use 512 byte blocks, coreutils use 1k */
  916. /* Actually, we round up by calculating (blocks + 1) / 2,
  917. * "+ 1" was done when we initialized blocks to 1 */
  918. return blocks >> 1;
  919. }
  920. #endif
  921. static void scan_and_display_dirs_recur(struct dnode **dn, int first)
  922. {
  923. unsigned nfiles;
  924. struct dnode **subdnp;
  925. for (; *dn; dn++) {
  926. if (G.show_dirname || (option_mask32 & OPT_R)) {
  927. if (!first)
  928. bb_putchar('\n');
  929. first = 0;
  930. printf("%s:\n", (*dn)->fullname);
  931. }
  932. subdnp = scan_one_dir((*dn)->fullname, &nfiles);
  933. #if ENABLE_DESKTOP
  934. if (option_mask32 & (OPT_s|OPT_l)) {
  935. if (option_mask32 & OPT_h) {
  936. printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n",
  937. /* print size, no fractions, use suffixes */
  938. make_human_readable_str(calculate_blocks(subdnp) * 1024,
  939. 0, 0)
  940. );
  941. } else {
  942. printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
  943. }
  944. }
  945. #endif
  946. if (nfiles > 0) {
  947. /* list all files at this level */
  948. sort_and_display_files(subdnp, nfiles);
  949. if (ENABLE_FEATURE_LS_RECURSIVE
  950. && (option_mask32 & OPT_R)
  951. ) {
  952. struct dnode **dnd;
  953. unsigned dndirs;
  954. /* recursive - list the sub-dirs */
  955. dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
  956. dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
  957. if (dndirs > 0) {
  958. dnsort(dnd, dndirs);
  959. scan_and_display_dirs_recur(dnd, 0);
  960. /* free the array of dnode pointers to the dirs */
  961. free(dnd);
  962. }
  963. }
  964. /* free the dnodes and the fullname mem */
  965. dfree(subdnp);
  966. }
  967. }
  968. }
  969. int ls_main(int argc UNUSED_PARAM, char **argv)
  970. { /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
  971. struct dnode **dnd;
  972. struct dnode **dnf;
  973. struct dnode **dnp;
  974. struct dnode *dn;
  975. struct dnode *cur;
  976. unsigned opt;
  977. unsigned nfiles;
  978. unsigned dnfiles;
  979. unsigned dndirs;
  980. unsigned i;
  981. #if ENABLE_FEATURE_LS_COLOR
  982. /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
  983. /* coreutils 6.10:
  984. * # ls --color=BOGUS
  985. * ls: invalid argument 'BOGUS' for '--color'
  986. * Valid arguments are:
  987. * 'always', 'yes', 'force'
  988. * 'never', 'no', 'none'
  989. * 'auto', 'tty', 'if-tty'
  990. * (and substrings: "--color=alwa" work too)
  991. */
  992. static const char color_str[] ALIGN1 =
  993. "always\0""yes\0""force\0"
  994. "auto\0""tty\0""if-tty\0";
  995. /* need to initialize since --color has _an optional_ argument */
  996. const char *color_opt = color_str; /* "always" */
  997. #endif
  998. #if ENABLE_LONG_OPTS
  999. static const char ls_longopts[] ALIGN1 =
  1000. "full-time\0" No_argument "\xff"
  1001. "group-directories-first\0" No_argument "\xfe"
  1002. IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
  1003. ;
  1004. #endif
  1005. INIT_G();
  1006. init_unicode();
  1007. #if ENABLE_FEATURE_LS_WIDTH
  1008. /* obtain the terminal width */
  1009. G_terminal_width = get_terminal_width(STDIN_FILENO);
  1010. /* go one less... */
  1011. G_terminal_width--;
  1012. #endif
  1013. /* process options */
  1014. opt = getopt32long(argv, "^"
  1015. ls_options
  1016. "\0"
  1017. /* -n and -g imply -l */
  1018. "nl:gl"
  1019. /* --full-time implies -l */
  1020. IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
  1021. /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
  1022. * in some pairs of opts, only last one takes effect:
  1023. */
  1024. IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
  1025. // ":m-l:l-m" - we don't have -m
  1026. IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
  1027. ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
  1028. ":C-1:1-C" /* bycols/oneline */
  1029. ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
  1030. IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
  1031. /* -w NUM: */
  1032. IF_FEATURE_LS_WIDTH(":w+")
  1033. , ls_longopts
  1034. IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
  1035. IF_FEATURE_LS_COLOR(, &color_opt)
  1036. );
  1037. #if 0 /* option bits debug */
  1038. bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first);
  1039. if (opt & OPT_c ) bb_error_msg("-c");
  1040. if (opt & OPT_l ) bb_error_msg("-l");
  1041. if (opt & OPT_H ) bb_error_msg("-H");
  1042. if (opt & OPT_color ) bb_error_msg("--color");
  1043. if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
  1044. if (opt & OPT_full_time ) bb_error_msg("--full-time");
  1045. exit(0);
  1046. #endif
  1047. #if ENABLE_SELINUX
  1048. if (opt & OPT_Z) {
  1049. if (!is_selinux_enabled())
  1050. option_mask32 &= ~OPT_Z;
  1051. }
  1052. #endif
  1053. #if ENABLE_FEATURE_LS_COLOR
  1054. /* set G_show_color = 1/0 */
  1055. if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) {
  1056. char *p = getenv("LS_COLORS");
  1057. /* LS_COLORS is unset, or (not empty && not "none") ? */
  1058. if (!p || (p[0] && strcmp(p, "none") != 0)) {
  1059. if (isatty(STDOUT_FILENO)) {
  1060. /* check isatty() last because it's expensive (syscall) */
  1061. G_show_color = 1;
  1062. }
  1063. }
  1064. }
  1065. if (opt & OPT_color) {
  1066. if (color_opt[0] == 'n')
  1067. G_show_color = 0;
  1068. else switch (index_in_substrings(color_str, color_opt)) {
  1069. case 3:
  1070. case 4:
  1071. case 5:
  1072. if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) {
  1073. case 0:
  1074. case 1:
  1075. case 2:
  1076. G_show_color = 1;
  1077. }
  1078. }
  1079. }
  1080. #endif
  1081. /* sort out which command line options take precedence */
  1082. if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
  1083. option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
  1084. if (!(opt & OPT_l)) { /* not -l? */
  1085. if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
  1086. /* when to sort by time? -t[cu] sorts by time even with -l */
  1087. /* (this is achieved by opt_flags[] element for -t) */
  1088. /* without -l, bare -c or -u enable sort too */
  1089. /* (with -l, bare -c or -u just select which time to show) */
  1090. if (opt & (OPT_c|OPT_u)) {
  1091. option_mask32 |= OPT_t;
  1092. }
  1093. }
  1094. }
  1095. /* choose a display format if one was not already specified by an option */
  1096. if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
  1097. option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
  1098. if (ENABLE_FTPD && applet_name[0] == 'f') {
  1099. /* ftpd secret backdoor. dirs first are much nicer */
  1100. option_mask32 |= OPT_dirs_first;
  1101. }
  1102. argv += optind;
  1103. if (!argv[0])
  1104. *--argv = (char*)".";
  1105. if (argv[1])
  1106. G.show_dirname = 1; /* 2 or more items? label directories */
  1107. /* stuff the command line file names into a dnode array */
  1108. dn = NULL;
  1109. nfiles = 0;
  1110. do {
  1111. cur = my_stat(*argv, *argv,
  1112. /* follow links on command line unless -l, -i, -s or -F: */
  1113. !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F))
  1114. /* ... or if -H: */
  1115. || (option_mask32 & OPT_H)
  1116. /* ... or if -L, but my_stat always follows links if -L */
  1117. );
  1118. argv++;
  1119. if (!cur)
  1120. continue;
  1121. /*cur->fname_allocated = 0; - already is */
  1122. cur->dn_next = dn;
  1123. dn = cur;
  1124. nfiles++;
  1125. } while (*argv);
  1126. /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
  1127. if (nfiles == 0)
  1128. return G.exit_code;
  1129. /* now that we know how many files there are
  1130. * allocate memory for an array to hold dnode pointers
  1131. */
  1132. dnp = dnalloc(nfiles);
  1133. for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
  1134. dnp[i] = dn; /* save pointer to node in array */
  1135. dn = dn->dn_next;
  1136. if (!dn)
  1137. break;
  1138. }
  1139. if (option_mask32 & OPT_d) {
  1140. sort_and_display_files(dnp, nfiles);
  1141. } else {
  1142. dnd = splitdnarray(dnp, SPLIT_DIR);
  1143. dnf = splitdnarray(dnp, SPLIT_FILE);
  1144. dndirs = count_dirs(dnp, SPLIT_DIR);
  1145. dnfiles = nfiles - dndirs;
  1146. if (dnfiles > 0) {
  1147. sort_and_display_files(dnf, dnfiles);
  1148. if (ENABLE_FEATURE_CLEAN_UP)
  1149. free(dnf);
  1150. }
  1151. if (dndirs > 0) {
  1152. dnsort(dnd, dndirs);
  1153. scan_and_display_dirs_recur(dnd, dnfiles == 0);
  1154. if (ENABLE_FEATURE_CLEAN_UP)
  1155. free(dnd);
  1156. }
  1157. }
  1158. if (ENABLE_FEATURE_CLEAN_UP)
  1159. dfree(dnp);
  1160. return G.exit_code;
  1161. }