blob: e69f1afd962652c0705042a85564b71814560197 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen9d3aba71999-10-06 09:04:55 +00002/*
Eric Andersencc8ed391999-10-05 16:24:54 +00003 * tiny-ls.c version 0.1.0: A minimalist 'ls'
4 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
Eric Andersen11c65522000-09-07 17:24:47 +00005 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencc8ed391999-10-05 16:24:54 +00007 */
8
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00009/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +000010 * To achieve a small memory footprint, this version of 'ls' doesn't do any
11 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000012 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000013 * linking in substantial chunks of libc can be disabled.
14 *
15 * Although I don't really want to add new features to this program to
16 * keep it small, I *am* interested to receive bug fixes and ways to make
17 * it more portable.
18 *
19 * KNOWN BUGS:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020020 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000021 *
Eric Andersencc8ed391999-10-05 16:24:54 +000022 * NON-OPTIMAL BEHAVIOUR:
23 * 1. autowidth reads directories twice
24 * 2. if you do a short directory listing without filetype characters
25 * appended, there's no need to stat each one
26 * PORTABILITY:
27 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000028 *
29 * [2009-03]
30 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000031 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000032#include "libbb.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020033#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000034
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000035
Denis Vlasenko99912ca2007-04-10 15:43:37 +000036/* This is a NOEXEC applet. Be very careful! */
37
Eric Andersenf1142c52001-02-20 06:16:29 +000038
Denis Vlasenko245f91b2009-03-09 22:37:23 +000039#if ENABLE_FTPD
40/* ftpd uses ls, and without timestamps Mozilla won't understand
41 * ftpd's LIST output.
42 */
43# undef CONFIG_FEATURE_LS_TIMESTAMPS
44# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000045# undef IF_FEATURE_LS_TIMESTAMPS
46# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +000047# define CONFIG_FEATURE_LS_TIMESTAMPS 1
48# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000049# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
50# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +000051#endif
52
53
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000054enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000055TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
56COLUMN_GAP = 2, /* includes the file type char */
57
58/* what is the overall style of the listing */
59STYLE_COLUMNS = 1 << 21, /* fill columns */
60STYLE_LONG = 2 << 21, /* one record per line, extended info */
61STYLE_SINGLE = 3 << 21, /* one record per line */
62STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +000063
Eric Andersen11c65522000-09-07 17:24:47 +000064/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
65/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000066LIST_INO = 1 << 0,
67LIST_BLOCKS = 1 << 1,
68LIST_MODEBITS = 1 << 2,
69LIST_NLINKS = 1 << 3,
70LIST_ID_NAME = 1 << 4,
71LIST_ID_NUMERIC = 1 << 5,
72LIST_CONTEXT = 1 << 6,
73LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +000074//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000075LIST_DATE_TIME = 1 << 9,
76LIST_FULLTIME = 1 << 10,
77LIST_FILENAME = 1 << 11,
78LIST_SYMLINK = 1 << 12,
79LIST_FILETYPE = 1 << 13,
80LIST_EXEC = 1 << 14,
81LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +000082
83/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000084DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +000085DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000086DISP_DOT = 1 << 17, /* show . and .. */
87DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
88DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
89DISP_ROWS = 1 << 20, /* print across rows */
90DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +000091
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000092/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
93SORT_FORWARD = 0, /* sort in reverse order */
94SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +000095
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000096SORT_NAME = 0, /* sort by file name */
97SORT_SIZE = 1 << 28, /* sort by file size */
98SORT_ATIME = 2 << 28, /* sort by last access time */
99SORT_CTIME = 3 << 28, /* sort by last change time */
100SORT_MTIME = 4 << 28, /* sort by last modification time */
101SORT_VERSION = 5 << 28, /* sort by version */
102SORT_EXT = 6 << 28, /* sort by file name extension */
103SORT_DIR = 7 << 28, /* sort by file or directory */
104SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000105
Eric Andersen11c65522000-09-07 17:24:47 +0000106/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000107TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
108TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
109TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000110
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000111FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000112
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000113LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000114
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000115LIST_SHORT = LIST_FILENAME,
116LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
117 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
118
119SPLIT_DIR = 1,
120SPLIT_FILE = 0,
121SPLIT_SUBDIR = 2,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000122};
Eric Andersencc8ed391999-10-05 16:24:54 +0000123
Denis Vlasenko248ce912009-03-03 14:09:04 +0000124/* "[-]Cadil1", POSIX mandated options, busybox always supports */
125/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
126/* "[-]Q" GNU option? busybox always supports */
127/* "[-]Ak" GNU options, busybox always supports */
128/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
129/* "[-]p", POSIX non-mandated options, busybox optionally supports */
130/* "[-]SXvThw", GNU options, busybox optionally supports */
131/* "[-]K", SELinux mandated options, busybox optionally supports */
132/* "[-]e", I think we made this one up */
133static const char ls_options[] ALIGN1 =
134 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000135 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
136 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
137 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
138 IF_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
139 IF_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
140 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
Denys Vlasenko55083632009-07-02 14:25:51 +0200141 IF_SELINUX("KZ") /* 2, 28 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000142 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000143 ;
144enum {
145 //OPT_C = (1 << 0),
146 //OPT_a = (1 << 1),
147 //OPT_d = (1 << 2),
148 //OPT_i = (1 << 3),
149 //OPT_l = (1 << 4),
150 //OPT_1 = (1 << 5),
151 OPT_g = (1 << 6),
152 //OPT_n = (1 << 7),
153 //OPT_s = (1 << 8),
154 //OPT_x = (1 << 9),
155 OPT_Q = (1 << 10),
156 //OPT_A = (1 << 11),
157 //OPT_k = (1 << 12),
Denys Vlasenko55083632009-07-02 14:25:51 +0200158 OPTBIT_color = 13
159 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
160 + 4 * ENABLE_FEATURE_LS_SORTFILES
161 + 2 * ENABLE_FEATURE_LS_FILETYPES
162 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
163 + 1 * ENABLE_FEATURE_LS_RECURSIVE
164 + 1 * ENABLE_FEATURE_HUMAN_READABLE
165 + 2 * ENABLE_SELINUX
166 + 2 * ENABLE_FEATURE_AUTOWIDTH,
167 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000168};
169
170enum {
171 LIST_MASK_TRIGGER = 0,
172 STYLE_MASK_TRIGGER = STYLE_MASK,
173 DISP_MASK_TRIGGER = DISP_ROWS,
174 SORT_MASK_TRIGGER = SORT_MASK,
175};
176
177/* TODO: simple toggles may be stored as OPT_xxx bits instead */
178static const unsigned opt_flags[] = {
179 LIST_SHORT | STYLE_COLUMNS, /* C */
180 DISP_HIDDEN | DISP_DOT, /* a */
181 DISP_NOLIST, /* d */
182 LIST_INO, /* i */
183 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
184 LIST_SHORT | STYLE_SINGLE, /* 1 */
185 0, /* g (don't show group) - handled via OPT_g */
186 LIST_ID_NUMERIC, /* n */
187 LIST_BLOCKS, /* s */
188 DISP_ROWS, /* x */
189 0, /* Q (quote filename) - handled via OPT_Q */
190 DISP_HIDDEN, /* A */
191 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
192#if ENABLE_FEATURE_LS_TIMESTAMPS
193 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
194 LIST_FULLTIME, /* e */
195 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
196 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
197#endif
198#if ENABLE_FEATURE_LS_SORTFILES
199 SORT_SIZE, /* S */
200 SORT_EXT, /* X */
201 SORT_REVERSE, /* r */
202 SORT_VERSION, /* v */
203#endif
204#if ENABLE_FEATURE_LS_FILETYPES
205 LIST_FILETYPE | LIST_EXEC, /* F */
206 LIST_FILETYPE, /* p */
207#endif
208#if ENABLE_FEATURE_LS_FOLLOWLINKS
209 FOLLOW_LINKS, /* L */
210#endif
211#if ENABLE_FEATURE_LS_RECURSIVE
212 DISP_RECURSIVE, /* R */
213#endif
214#if ENABLE_FEATURE_HUMAN_READABLE
215 LS_DISP_HR, /* h */
216#endif
217#if ENABLE_SELINUX
218 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
219#endif
220#if ENABLE_SELINUX
221 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
222#endif
223 (1U<<31)
224 /* options after Z are not processed through opt_flags:
225 * T, w - ignored
226 */
227};
228
229
Eric Andersen11c65522000-09-07 17:24:47 +0000230/*
231 * a directory entry and its stat info are stored here
232 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200233struct dnode {
234 const char *name; /* the dir entry name */
235 const char *fullname; /* the dir entry name */
236 struct dnode *next; /* point at the next node */
237 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000238 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000239 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000240};
Eric Andersen11c65522000-09-07 17:24:47 +0000241
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000242struct globals {
243#if ENABLE_FEATURE_LS_COLOR
244 smallint show_color;
245#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000246 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000247 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000248#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000249 unsigned tabstops; // = COLUMN_GAP;
250 unsigned terminal_width; // = TERMINAL_WIDTH;
251#endif
252#if ENABLE_FEATURE_LS_TIMESTAMPS
253 /* Do time() just once. Saves one syscall per file for "ls -l" */
254 time_t current_time_t;
255#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100256} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000257#define G (*(struct globals*)&bb_common_bufsiz1)
258#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200259# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000260#else
261enum { show_color = 0 };
262#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200263#define exit_code (G.exit_code )
264#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000265#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200266# define tabstops (G.tabstops )
267# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000268#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000269enum {
270 tabstops = COLUMN_GAP,
271 terminal_width = TERMINAL_WIDTH,
272};
Eric Andersen11c65522000-09-07 17:24:47 +0000273#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000274#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000275#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200276 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000277 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000278 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
279 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
280 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000281} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000282
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000283
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000284static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000285{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000286 struct stat dstat;
287 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000288 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000289
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000290 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000291#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000292 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000293 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000294 }
Eric Andersen9e480452003-07-03 10:07:04 +0000295#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000296 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000297 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000298 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000299 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000300 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000301 } else {
302#if ENABLE_SELINUX
303 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000304 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000305 }
Eric Andersen9e480452003-07-03 10:07:04 +0000306#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000307 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000308 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000309 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000310 return 0;
311 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000312 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000313
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200314 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000315 cur->fullname = fullname;
316 cur->name = name;
317 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000318 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000319 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000320}
321
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000322/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
323 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
324 * 3/7:multiplexed char/block device)
325 * and we use 0 for unknown and 15 for executables (see below) */
326#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
327#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
328#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
329/* 036 black foreground 050 black background
330 037 red foreground 051 red background
331 040 green foreground 052 green background
332 041 brown foreground 053 brown background
333 042 blue foreground 054 blue background
334 043 magenta (purple) foreground 055 magenta background
335 044 cyan (light blue) foreground 056 cyan background
336 045 gray foreground 057 white background
337*/
338#define COLOR(mode) ( \
339 /*un fi chr dir blk file link sock exe */ \
340 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
341 [TYPEINDEX(mode)])
342/* Select normal (0) [actually "reset all"] or bold (1)
343 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
344 * let's use 7 for "impossible" types, just for fun)
345 * Note: coreutils 6.9 uses inverted red for setuid binaries.
346 */
347#define ATTR(mode) ( \
348 /*un fi chr dir blk file link sock exe */ \
349 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
350 [TYPEINDEX(mode)])
351
Denis Vlasenko5c759602006-10-28 12:37:16 +0000352#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000353/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000354static char fgcolor(mode_t mode)
355{
Rob Landley9947a242006-06-15 22:11:10 +0000356 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000357 return COLOR(0xF000); /* File is executable ... */
358 return COLOR(mode);
359}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000360static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000361{
Rob Landley9947a242006-06-15 22:11:10 +0000362 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000363 return ATTR(0xF000); /* File is executable ... */
364 return ATTR(mode);
365}
366#endif
367
Denis Vlasenko5c759602006-10-28 12:37:16 +0000368#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000369static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000370{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000371 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000372 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000373 if (S_ISDIR(mode))
374 return '/';
375 if (!(all_fmt & LIST_EXEC))
376 return '\0';
377 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000378 return '*';
379 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000380}
381#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000382
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200383static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000384{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200385 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000386
Denis Vlasenko5c759602006-10-28 12:37:16 +0000387 if (!dn)
388 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200389
390 dirs = all = 0;
391 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000392 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200393
394 all++;
395 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000396 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200397 name = (*dn)->name;
398 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
399 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200400 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000401 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000402 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000403 }
Eric Andersen11c65522000-09-07 17:24:47 +0000404 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200405 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000406}
407
Eric Andersen11c65522000-09-07 17:24:47 +0000408/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200409static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000410{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000411 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000412 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000413
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200414 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000415 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000416}
417
Denis Vlasenko5c759602006-10-28 12:37:16 +0000418#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200419static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000420{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200421 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000422
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000423 if (dnp == NULL)
424 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000425
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200426 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000427 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200428 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200429 free((char*)cur->fullname);
430 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000431 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200432 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000433}
Rob Landleyc44bc982006-05-28 01:19:06 +0000434#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000435#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000436#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000437
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200438/* Returns NULL-terminated malloced vector of pointers (or NULL) */
439static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000440{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200441 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000442 struct dnode **dnp;
443
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200444 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000445 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000446
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200447 /* count how many dirs or files there are */
448 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000449
450 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000451 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000452
453 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200454 for (d = 0; *dn; dn++) {
455 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000456 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200457
Denis Vlasenko5c759602006-10-28 12:37:16 +0000458 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
459 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200460 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000461 if ((which & SPLIT_DIR)
462 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
463 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200464 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000465 }
466 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200467 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000468 }
469 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000470 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000471}
472
Denis Vlasenko5c759602006-10-28 12:37:16 +0000473#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000474static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000475{
Rob Landley425e7582006-05-03 20:22:03 +0000476 struct dnode *d1 = *(struct dnode **)a;
477 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000478 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100479 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000480
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000481 dif = 0; /* assume SORT_NAME */
482 // TODO: use pre-initialized function pointer
483 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000484 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100485 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000486 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100487 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000488 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100489 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000490 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100491 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000492 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000493 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000494 /* } else if (sort_opts == SORT_VERSION) { */
495 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000496 }
Eric Andersen11c65522000-09-07 17:24:47 +0000497 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100498 /* sort by name, or tie_breaker for other sorts */
499 if (ENABLE_LOCALE_SUPPORT)
500 dif = strcoll(d1->name, d2->name);
501 else
502 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000503 }
504
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100505 /* Make dif fit into an int */
506 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100507 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
508 /* shift leaving only "int" worth of bits */
509 if (dif != 0) {
510 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100511 }
Eric Andersen11c65522000-09-07 17:24:47 +0000512 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100513
514 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000515}
516
Rob Landley425e7582006-05-03 20:22:03 +0000517static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000518{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000519 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000520}
Rob Landleyea224be2006-06-18 20:20:07 +0000521#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000522#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000523#endif
524
Rob Landleyea224be2006-06-18 20:20:07 +0000525
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100526static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000527{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100528 unsigned len;
529 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000530
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100531 // TODO: quote tab as \t, etc, if -Q
532 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000533
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100534 if (!(option_mask32 & OPT_Q)) {
535 return uni_stat.unicode_width;
536 }
537
538 len = 2 + uni_stat.unicode_width;
539 while (*name) {
540 if (*name == '"' || *name == '\\') {
541 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000542 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100543 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000544 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100545 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000546}
547
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000548
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100549/* Return the number of used columns.
550 * Note that only STYLE_COLUMNS uses return value.
551 * STYLE_SINGLE and STYLE_LONG don't care.
552 * coreutils 7.2 also supports:
553 * ls -b (--escape) = octal escapes (although it doesn't look like working)
554 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200555 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100556static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200557{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100558 unsigned len;
559 uni_stat_t uni_stat;
560
561 // TODO: quote tab as \t, etc, if -Q
562 name = printable_string(&uni_stat, name);
563
564 if (!(option_mask32 & OPT_Q)) {
565 fputs(name, stdout);
566 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200567 }
568
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100569 len = 2 + uni_stat.unicode_width;
570 putchar('"');
571 while (*name) {
572 if (*name == '"' || *name == '\\') {
573 putchar('\\');
574 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000575 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700576 putchar(*name);
577 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000578 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100579 putchar('"');
580 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000581}
582
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100583/* Return the number of used columns.
584 * Note that only STYLE_COLUMNS uses return value,
585 * STYLE_SINGLE and STYLE_LONG don't care.
586 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200587static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000588{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200589 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000590 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000591#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000592 struct stat info;
593 char append;
594#endif
595
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200596 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000597 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000598 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200599 */
Eric Andersen11c65522000-09-07 17:24:47 +0000600
Denis Vlasenko5c759602006-10-28 12:37:16 +0000601#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000602 append = append_char(dn->dstat.st_mode);
603#endif
604
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000605 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100606 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000607 if (all_fmt & LIST_SYMLINK)
608 if (S_ISLNK(dn->dstat.st_mode))
609 lpath = xmalloc_readlink_or_warn(dn->fullname);
610
611 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100612 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000613 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100614 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000615 if (all_fmt & LIST_MODEBITS)
616 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
617 if (all_fmt & LIST_NLINKS)
618 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000619#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000620 if (all_fmt & LIST_ID_NAME) {
621 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100622 column += printf("%-8.8s ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000623 get_cached_username(dn->dstat.st_uid));
624 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100625 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000626 get_cached_username(dn->dstat.st_uid),
627 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000628 }
629 }
Eric Andersen11c65522000-09-07 17:24:47 +0000630#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000631 if (all_fmt & LIST_ID_NUMERIC) {
632 if (option_mask32 & OPT_g)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100633 column += printf("%-8u ", (int) dn->dstat.st_uid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000634 else
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100635 column += printf("%-8u %-8u ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000636 (int) dn->dstat.st_uid,
637 (int) dn->dstat.st_gid);
638 }
639 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
640 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
641 column += printf("%4u, %3u ",
642 (int) major(dn->dstat.st_rdev),
643 (int) minor(dn->dstat.st_rdev));
644 } else {
645 if (all_fmt & LS_DISP_HR) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100646 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200647 /* print st_size, show one fractional, use suffixes */
648 make_human_readable_str(dn->dstat.st_size, 1, 0)
649 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000650 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000651 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000652 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000653 }
654 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000655#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100656 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
657 char *filetime;
658 time_t ttime = dn->dstat.st_mtime;
659 if (all_fmt & TIME_ACCESS)
660 ttime = dn->dstat.st_atime;
661 if (all_fmt & TIME_CHANGE)
662 ttime = dn->dstat.st_ctime;
663 filetime = ctime(&ttime);
664 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
665 if (all_fmt & LIST_FULLTIME)
666 column += printf("%.24s ", filetime);
667 else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000668 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100669 time_t age = current_time_t - ttime;
670 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000671 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
672 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100673 printf("%.5s ", filetime + 11);
674 } else { /* year. buggy if year > 9999 ;) */
675 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000676 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000677 column += 13;
678 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100679 }
Eric Andersen11c65522000-09-07 17:24:47 +0000680#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000681#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000682 if (all_fmt & LIST_CONTEXT) {
683 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
684 freecon(dn->sid);
685 }
Eric Andersen9e480452003-07-03 10:07:04 +0000686#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000687 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000688#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000689 if (show_color) {
690 info.st_mode = 0; /* for fgcolor() */
691 lstat(dn->fullname, &info);
692 printf("\033[%u;%um", bold(info.st_mode),
693 fgcolor(info.st_mode));
694 }
695#endif
696 column += print_name(dn->name);
697 if (show_color) {
698 printf("\033[0m");
699 }
700 }
701 if (all_fmt & LIST_SYMLINK) {
702 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
703 printf(" -> ");
704#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
705#if ENABLE_FEATURE_LS_COLOR
706 info.st_mode = 0; /* for fgcolor() */
707#endif
708 if (stat(dn->fullname, &info) == 0) {
709 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000710 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000711#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000712#if ENABLE_FEATURE_LS_COLOR
713 if (show_color) {
714 printf("\033[%u;%um", bold(info.st_mode),
715 fgcolor(info.st_mode));
716 }
717#endif
718 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000719 if (show_color) {
720 printf("\033[0m");
721 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000722 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000723 }
724 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000725#if ENABLE_FEATURE_LS_FILETYPES
726 if (all_fmt & LIST_FILETYPE) {
727 if (append) {
728 putchar(append);
729 column++;
730 }
731 }
732#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000733
Glenn L McGrath4d001292003-01-06 01:11:50 +0000734 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000735}
736
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100737static void showfiles(struct dnode **dn, unsigned nfiles)
738{
739 unsigned i, ncols, nrows, row, nc;
740 unsigned column = 0;
741 unsigned nexttab = 0;
742 unsigned column_width = 0; /* used only by STYLE_COLUMNS */
743
744 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
745 ncols = 1;
746 } else {
747 /* find the longest file name, use that as the column width */
748 for (i = 0; dn[i]; i++) {
749 int len = calc_name_len(dn[i]->name);
750 if (column_width < len)
751 column_width = len;
752 }
753 column_width += tabstops +
754 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
755 ((all_fmt & LIST_INO) ? 8 : 0) +
756 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
757 ncols = (int) (terminal_width / column_width);
758 }
759
760 if (ncols > 1) {
761 nrows = nfiles / ncols;
762 if (nrows * ncols < nfiles)
763 nrows++; /* round up fractionals */
764 } else {
765 nrows = nfiles;
766 ncols = 1;
767 }
768
769 for (row = 0; row < nrows; row++) {
770 for (nc = 0; nc < ncols; nc++) {
771 /* reach into the array based on the column and row */
772 if (all_fmt & DISP_ROWS)
773 i = (row * ncols) + nc; /* display across row */
774 else
775 i = (nc * nrows) + row; /* display by column */
776 if (i < nfiles) {
777 if (column > 0) {
778 nexttab -= column;
779 printf("%*s", nexttab, "");
780 column += nexttab;
781 }
782 nexttab = column + column_width;
783 column += list_single(dn[i]);
784 }
785 }
786 putchar('\n');
787 column = 0;
788 }
789}
790
791
792#if ENABLE_DESKTOP
793/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
794 * If any of the -l, -n, -s options is specified, each list
795 * of files within the directory shall be preceded by a
796 * status line indicating the number of file system blocks
797 * occupied by files in the directory in 512-byte units if
798 * the -k option is not specified, or 1024-byte units if the
799 * -k option is specified, rounded up to the next integral
800 * number of units.
801 */
802/* by Jorgen Overgaard (jorgen AT antistaten.se) */
803static off_t calculate_blocks(struct dnode **dn)
804{
805 uoff_t blocks = 1;
806 if (dn) {
807 while (*dn) {
808 /* st_blocks is in 512 byte blocks */
809 blocks += (*dn)->dstat.st_blocks;
810 dn++;
811 }
812 }
813
814 /* Even though standard says use 512 byte blocks, coreutils use 1k */
815 /* Actually, we round up by calculating (blocks + 1) / 2,
816 * "+ 1" was done when we initialized blocks to 1 */
817 return blocks >> 1;
818}
819#endif
820
821
822static struct dnode **list_dir(const char *, unsigned *);
823
824static void showdirs(struct dnode **dn, int first)
825{
826 unsigned nfiles;
827 unsigned dndirs;
828 struct dnode **subdnp;
829 struct dnode **dnd;
830
831 /* Never happens:
832 if (dn == NULL || ndirs < 1) {
833 return;
834 }
835 */
836
837 for (; *dn; dn++) {
838 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
839 if (!first)
840 bb_putchar('\n');
841 first = 0;
842 printf("%s:\n", (*dn)->fullname);
843 }
844 subdnp = list_dir((*dn)->fullname, &nfiles);
845#if ENABLE_DESKTOP
846 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
847 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
848#endif
849 if (nfiles > 0) {
850 /* list all files at this level */
851 dnsort(subdnp, nfiles);
852 showfiles(subdnp, nfiles);
853 if (ENABLE_FEATURE_LS_RECURSIVE
854 && (all_fmt & DISP_RECURSIVE)
855 ) {
856 /* recursive - list the sub-dirs */
857 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
858 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
859 if (dndirs > 0) {
860 dnsort(dnd, dndirs);
861 showdirs(dnd, 0);
862 /* free the array of dnode pointers to the dirs */
863 free(dnd);
864 }
865 }
866 /* free the dnodes and the fullname mem */
867 dfree(subdnp);
868 }
869 }
870}
871
872
873/* Returns NULL-terminated malloced vector of pointers (or NULL) */
874static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
875{
876 struct dnode *dn, *cur, **dnp;
877 struct dirent *entry;
878 DIR *dir;
879 unsigned i, nfiles;
880
881 /* Never happens:
882 if (path == NULL)
883 return NULL;
884 */
885
886 *nfiles_p = 0;
887 dir = warn_opendir(path);
888 if (dir == NULL) {
889 exit_code = EXIT_FAILURE;
890 return NULL; /* could not open the dir */
891 }
892 dn = NULL;
893 nfiles = 0;
894 while ((entry = readdir(dir)) != NULL) {
895 char *fullname;
896
897 /* are we going to list the file- it may be . or .. or a hidden file */
898 if (entry->d_name[0] == '.') {
899 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
900 && !(all_fmt & DISP_DOT)
901 ) {
902 continue;
903 }
904 if (!(all_fmt & DISP_HIDDEN))
905 continue;
906 }
907 fullname = concat_path_file(path, entry->d_name);
908 cur = my_stat(fullname, bb_basename(fullname), 0);
909 if (!cur) {
910 free(fullname);
911 continue;
912 }
913 cur->fname_allocated = 1;
914 cur->next = dn;
915 dn = cur;
916 nfiles++;
917 }
918 closedir(dir);
919
920 if (dn == NULL)
921 return NULL;
922
923 /* now that we know how many files there are
924 * allocate memory for an array to hold dnode pointers
925 */
926 *nfiles_p = nfiles;
927 dnp = dnalloc(nfiles);
928 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
929 dnp[i] = dn; /* save pointer to node in array */
930 dn = dn->next;
931 if (!dn)
932 break;
933 }
934
935 return dnp;
936}
937
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000938
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000939int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000940{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000941 struct dnode **dnd;
942 struct dnode **dnf;
943 struct dnode **dnp;
944 struct dnode *dn;
945 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000946 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200947 unsigned nfiles;
948 unsigned dnfiles;
949 unsigned dndirs;
950 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200951#if ENABLE_FEATURE_LS_COLOR
952 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
953 /* coreutils 6.10:
954 * # ls --color=BOGUS
955 * ls: invalid argument 'BOGUS' for '--color'
956 * Valid arguments are:
957 * 'always', 'yes', 'force'
958 * 'never', 'no', 'none'
959 * 'auto', 'tty', 'if-tty'
960 * (and substrings: "--color=alwa" work too)
961 */
962 static const char ls_longopts[] ALIGN1 =
963 "color\0" Optional_argument "\xff"; /* no short equivalent */
964 static const char color_str[] ALIGN1 =
965 "always\0""yes\0""force\0"
966 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000967 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200968 const char *color_opt = color_str; /* "always" */
969#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000970
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000971 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000972
Denys Vlasenko28055022010-01-04 20:49:58 +0100973 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200974
Rob Landley2b8a05a2006-06-20 17:43:01 +0000975 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000976 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000977
Denis Vlasenko5c759602006-10-28 12:37:16 +0000978#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200979 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000980 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200981 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000982 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000983#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000984
Eric Andersen11c65522000-09-07 17:24:47 +0000985 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200986 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000987#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000988 opt_complementary = "T+:w+"; /* -T N, -w N */
989 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000990 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000991#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000992 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000993#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000994 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000995 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000996 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000997
Denis Vlasenko5c759602006-10-28 12:37:16 +0000998 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000999 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001000 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001001 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001002 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001003 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001004 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001005 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001006 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001007 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001008 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +00001009 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +00001010 /* huh?? opt cannot be 'l' */
1011 //if (LS_DISP_HR && opt == 'l')
1012 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001013 all_fmt |= flags;
1014 }
Eric Andersen11c65522000-09-07 17:24:47 +00001015 }
1016
Denis Vlasenko5c759602006-10-28 12:37:16 +00001017#if ENABLE_FEATURE_LS_COLOR
1018 /* find color bit value - last position for short getopt */
1019 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1020 char *p = getenv("LS_COLORS");
1021 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001022 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001023 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001024 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001025 if (opt & OPT_color) {
1026 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001027 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001028 else switch (index_in_substrings(color_str, color_opt)) {
1029 case 3:
1030 case 4:
1031 case 5:
1032 if (isatty(STDOUT_FILENO)) {
1033 case 0:
1034 case 1:
1035 case 2:
1036 show_color = 1;
1037 }
1038 }
Paul Fox156dc412005-08-01 19:33:30 +00001039 }
1040#endif
1041
Eric Andersen11c65522000-09-07 17:24:47 +00001042 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001043 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001044 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001045 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1046 if (all_fmt & TIME_CHANGE)
1047 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1048 if (all_fmt & TIME_ACCESS)
1049 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1050 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001051 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1052 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001053 if (ENABLE_FEATURE_LS_USERNAME)
1054 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1055 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001056
Eric Andersen11c65522000-09-07 17:24:47 +00001057 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001058 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001059 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001060
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001061 argv += optind;
1062 if (!argv[0])
1063 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001064
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001065 if (argv[1])
1066 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001067
Denis Vlasenko5c759602006-10-28 12:37:16 +00001068 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001069 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001070 nfiles = 0;
1071 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001072 /* NB: follow links on command line unless -l or -s */
1073 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001074 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001075 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001076 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001077 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001078 cur->next = dn;
1079 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001080 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001081 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001082
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001083 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1084 if (nfiles == 0)
1085 return exit_code;
1086
Eric Andersen11c65522000-09-07 17:24:47 +00001087 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001088 * allocate memory for an array to hold dnode pointers
1089 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001090 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001091 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1092 dnp[i] = dn; /* save pointer to node in array */
1093 dn = dn->next;
1094 if (!dn)
1095 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001096 }
1097
Manuel Novoa III cad53642003-03-19 09:13:01 +00001098 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001099 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001100 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001101 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001102 dnd = splitdnarray(dnp, SPLIT_DIR);
1103 dnf = splitdnarray(dnp, SPLIT_FILE);
1104 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001105 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001106 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001107 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001108 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001109 if (ENABLE_FEATURE_CLEAN_UP)
1110 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001111 }
1112 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001113 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001114 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001115 if (ENABLE_FEATURE_CLEAN_UP)
1116 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001117 }
1118 }
Rob Landley26314862006-05-02 19:46:52 +00001119 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001120 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001121 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001122}