blob: 344f8ffcb6169047b4c3f1fe74ff27d01a51ffc1 [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 *
Eric Andersencc8ed391999-10-05 16:24:54 +00006 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21/*
22 * To achieve a small memory footprint, this version of 'ls' doesn't do any
23 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000024 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000025 * linking in substantial chunks of libc can be disabled.
26 *
27 * Although I don't really want to add new features to this program to
28 * keep it small, I *am* interested to receive bug fixes and ways to make
29 * it more portable.
30 *
31 * KNOWN BUGS:
Erik Andersen9ffdaa62000-02-11 21:55:04 +000032 * 1. ls -l of a directory doesn't give "total <blocks>" header
33 * 2. ls of a symlink to a directory doesn't list directory contents
34 * 3. hidden files can make column width too large
35 *
Eric Andersencc8ed391999-10-05 16:24:54 +000036 * NON-OPTIMAL BEHAVIOUR:
37 * 1. autowidth reads directories twice
38 * 2. if you do a short directory listing without filetype characters
39 * appended, there's no need to stat each one
40 * PORTABILITY:
41 * 1. requires lstat (BSD) - how do you do it without?
42 */
43
Eric Andersene57d54b2001-01-30 18:03:11 +000044enum {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +000045 TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
46 COLUMN_WIDTH = 14, /* default if AUTOWIDTH not defined */
47 COLUMN_GAP = 2, /* includes the file type char */
Eric Andersene57d54b2001-01-30 18:03:11 +000048};
49
Eric Andersencc8ed391999-10-05 16:24:54 +000050
51/************************************************************************/
52
Eric Andersen11c65522000-09-07 17:24:47 +000053#include <sys/types.h>
54#include <sys/stat.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000055#include <stdio.h>
56#include <unistd.h>
57#include <dirent.h>
58#include <errno.h>
59#include <stdio.h>
Eric Andersen11c65522000-09-07 17:24:47 +000060#include <string.h>
Eric Andersened3ef502001-01-27 08:24:39 +000061#include <stdlib.h>
Eric Andersen5307eca2001-01-26 01:52:43 +000062#include <fcntl.h>
63#include <signal.h>
Eric Andersen8d79ce82001-07-22 23:00:15 +000064#include <termios.h>
Eric Andersen5307eca2001-01-26 01:52:43 +000065#include <sys/ioctl.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000066#include "busybox.h"
Eric Andersen5307eca2001-01-26 01:52:43 +000067
Eric Andersenbdfd0d72001-10-24 05:00:29 +000068#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Eric Andersenf1142c52001-02-20 06:16:29 +000069#include <time.h>
70#endif
71
Eric Andersen30f64c32001-01-30 19:23:46 +000072#ifndef MAJOR
Erik Andersen1ad302a2000-03-24 00:54:46 +000073#define MAJOR(dev) (((dev)>>8)&0xff)
74#define MINOR(dev) ((dev)&0xff)
75#endif
76
Eric Andersen11c65522000-09-07 17:24:47 +000077/* what is the overall style of the listing */
Mark Whitley59ab0252001-01-23 22:30:04 +000078enum {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +000079 STYLE_AUTO = 0,
80 STYLE_LONG = 1, /* one record per line, extended info */
81 STYLE_SINGLE = 2, /* one record per line */
82 STYLE_COLUMNS = 3 /* fill columns */
Mark Whitley59ab0252001-01-23 22:30:04 +000083};
Eric Andersencc8ed391999-10-05 16:24:54 +000084
Eric Andersen11c65522000-09-07 17:24:47 +000085/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
86/* what file information will be listed */
87#define LIST_INO (1<<0)
88#define LIST_BLOCKS (1<<1)
89#define LIST_MODEBITS (1<<2)
90#define LIST_NLINKS (1<<3)
91#define LIST_ID_NAME (1<<4)
92#define LIST_ID_NUMERIC (1<<5)
93#define LIST_SIZE (1<<6)
94#define LIST_DEV (1<<7)
95#define LIST_DATE_TIME (1<<8)
96#define LIST_FULLTIME (1<<9)
97#define LIST_FILENAME (1<<10)
98#define LIST_SYMLINK (1<<11)
99#define LIST_FILETYPE (1<<12)
100#define LIST_EXEC (1<<13)
101
102/* what files will be displayed */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000103#define DISP_NORMAL (0) /* show normal filenames */
Eric Andersen11c65522000-09-07 17:24:47 +0000104#define DISP_DIRNAME (1<<0) /* 2 or more items? label directories */
105#define DISP_HIDDEN (1<<1) /* show filenames starting with . */
106#define DISP_DOT (1<<2) /* show . and .. */
107#define DISP_NOLIST (1<<3) /* show directory as itself, not contents */
108#define DISP_RECURSIVE (1<<4) /* show directory and everything below it */
109#define DISP_ROWS (1<<5) /* print across rows */
110
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000111#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen11c65522000-09-07 17:24:47 +0000112/* how will the files be sorted */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000113static const int SORT_FORWARD = 0; /* sort in reverse order */
114static const int SORT_REVERSE = 1; /* sort in reverse order */
115static const int SORT_NAME = 2; /* sort by file name */
116static const int SORT_SIZE = 3; /* sort by file size */
117static const int SORT_ATIME = 4; /* sort by last access time */
118static const int SORT_CTIME = 5; /* sort by last change time */
119static const int SORT_MTIME = 6; /* sort by last modification time */
120static const int SORT_VERSION = 7; /* sort by version */
121static const int SORT_EXT = 8; /* sort by file name extension */
122static const int SORT_DIR = 9; /* sort by file or directory */
Eric Andersencc8ed391999-10-05 16:24:54 +0000123#endif
124
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000125#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000126/* which of the three times will be used */
Mark Whitley59ab0252001-01-23 22:30:04 +0000127static const int TIME_MOD = 0;
128static const int TIME_CHANGE = 1;
129static const int TIME_ACCESS = 2;
Eric Andersencc8ed391999-10-05 16:24:54 +0000130#endif
131
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000132#define LIST_SHORT (LIST_FILENAME)
133#define LIST_ISHORT (LIST_INO | LIST_FILENAME)
134#define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
135 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK)
136#define LIST_ILONG (LIST_INO | LIST_LONG)
Eric Andersencc8ed391999-10-05 16:24:54 +0000137
Mark Whitley59ab0252001-01-23 22:30:04 +0000138static const int SPLIT_DIR = 0;
139static const int SPLIT_FILE = 1;
140static const int SPLIT_SUBDIR = 2;
Eric Andersencc8ed391999-10-05 16:24:54 +0000141
Eric Andersen11c65522000-09-07 17:24:47 +0000142#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
143#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000144
Eric Andersend598d412002-04-27 09:19:39 +0000145#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000146# define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
Eric Andersen11c65522000-09-07 17:24:47 +0000147#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000148
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000149/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
150#ifdef CONFIG_FEATURE_LS_COLOR
151static int show_color = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000152
153#define COLOR(mode) ("\000\043\043\043\042\000\043\043"\
154 "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
155#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\
156 "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000157#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000158
Eric Andersen11c65522000-09-07 17:24:47 +0000159/*
160 * a directory entry and its stat info are stored here
161 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000162struct dnode { /* the basic node */
163 char *name; /* the dir entry name */
164 char *fullname; /* the dir entry name */
165 struct stat dstat; /* the file stat info */
166 struct dnode *next; /* point at the next node */
Eric Andersen11c65522000-09-07 17:24:47 +0000167};
168typedef struct dnode dnode_t;
169
Eric Andersen3e6ff902001-03-09 21:24:12 +0000170static struct dnode **list_dir(char *);
171static struct dnode **dnalloc(int);
172static int list_single(struct dnode *);
Eric Andersen11c65522000-09-07 17:24:47 +0000173
Mark Whitley59ab0252001-01-23 22:30:04 +0000174static unsigned int disp_opts;
175static unsigned int style_fmt;
176static unsigned int list_fmt;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000177
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000178#ifdef CONFIG_FEATURE_LS_SORTFILES
Mark Whitley59ab0252001-01-23 22:30:04 +0000179static unsigned int sort_opts;
180static unsigned int sort_order;
Eric Andersen11c65522000-09-07 17:24:47 +0000181#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000182#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Mark Whitley59ab0252001-01-23 22:30:04 +0000183static unsigned int time_fmt;
Eric Andersen11c65522000-09-07 17:24:47 +0000184#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000185#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000186static unsigned int follow_links = FALSE;
Matt Kraaia2f2a8f2000-09-22 03:11:47 +0000187#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000188
189static unsigned short column = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000190
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000191#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersene57d54b2001-01-30 18:03:11 +0000192static unsigned short terminal_width = TERMINAL_WIDTH;
193static unsigned short column_width = COLUMN_WIDTH;
194static unsigned short tabstops = COLUMN_GAP;
Eric Andersenf5d5e772001-01-24 23:34:48 +0000195#else
Eric Andersena528dc72001-01-26 18:30:12 +0000196static unsigned short column_width = COLUMN_WIDTH;
Eric Andersen11c65522000-09-07 17:24:47 +0000197#endif
Eric Andersencc8ed391999-10-05 16:24:54 +0000198
Matt Kraai33fdae52000-10-13 17:59:43 +0000199static int status = EXIT_SUCCESS;
200
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000201#ifdef CONFIG_FEATURE_HUMAN_READABLE
Eric Andersen3e6ff902001-03-09 21:24:12 +0000202static unsigned long ls_disp_hr = 0;
Richard June6d0921c2001-01-22 22:35:38 +0000203#endif
204
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000205static int my_stat(struct dnode *cur)
206{
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000207#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
Matt Kraai1f0c4362001-12-20 23:13:26 +0000208 if (follow_links) {
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000209 if (stat(cur->fullname, &cur->dstat)) {
Matt Kraai1fa1ade2000-12-18 03:57:16 +0000210 perror_msg("%s", cur->fullname);
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000211 status = EXIT_FAILURE;
212 free(cur->fullname);
213 free(cur);
214 return -1;
215 }
216 } else
217#endif
218 if (lstat(cur->fullname, &cur->dstat)) {
Matt Kraai1fa1ade2000-12-18 03:57:16 +0000219 perror_msg("%s", cur->fullname);
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000220 status = EXIT_FAILURE;
221 free(cur->fullname);
222 free(cur);
223 return -1;
224 }
225 return 0;
226}
227
Eric Andersencc8ed391999-10-05 16:24:54 +0000228static void newline(void)
229{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000230 if (column > 0) {
231 putchar('\n');
232 column = 0;
233 }
Eric Andersen79565b62000-08-11 18:10:21 +0000234}
235
Eric Andersen11c65522000-09-07 17:24:47 +0000236/*----------------------------------------------------------------------*/
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000237#ifdef CONFIG_FEATURE_LS_COLOR
238static char fgcolor(mode_t mode)
239{
240 /* Check wheter the file is existing (if so, color it red!) */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000241 if (errno == ENOENT) {
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000242 errno = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000243 return '\037';
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000244 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000245 if (LIST_EXEC && S_ISREG(mode)
246 && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000247 return COLOR(0xF000); /* File is executable ... */
248 return COLOR(mode);
249}
250
251/*----------------------------------------------------------------------*/
252static char bgcolor(mode_t mode)
253{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000254 if (LIST_EXEC && S_ISREG(mode)
255 && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000256 return ATTR(0xF000); /* File is executable ... */
257 return ATTR(mode);
258}
259#endif
260
261/*----------------------------------------------------------------------*/
Eric Andersend598d412002-04-27 09:19:39 +0000262#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
Eric Andersen11c65522000-09-07 17:24:47 +0000263static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000264{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000265 if (!(list_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000266 return '\0';
267 if ((list_fmt & LIST_EXEC) && S_ISREG(mode)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000268 && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
269 return '*';
270 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000271}
272#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000273
Eric Andersen11c65522000-09-07 17:24:47 +0000274/*----------------------------------------------------------------------*/
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000275static void nexttabstop(void)
Eric Andersen11c65522000-09-07 17:24:47 +0000276{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000277 static short nexttab = 0;
278 int n = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000279
280 if (column > 0) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000281 n = nexttab - column;
282 if (n < 1)
283 n = 1;
Eric Andersen11c65522000-09-07 17:24:47 +0000284 while (n--) {
Matt Kraai12f417e2001-01-18 02:57:08 +0000285 putchar(' ');
Eric Andersen11c65522000-09-07 17:24:47 +0000286 column++;
287 }
288 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000289 nexttab = column + column_width + COLUMN_GAP;
Eric Andersen11c65522000-09-07 17:24:47 +0000290}
291
292/*----------------------------------------------------------------------*/
Eric Andersencf1189f2000-11-29 21:52:06 +0000293static int is_subdir(struct dnode *dn)
294{
295 return (S_ISDIR(dn->dstat.st_mode) && strcmp(dn->name, ".") != 0 &&
296 strcmp(dn->name, "..") != 0);
297}
298
Eric Andersen3e6ff902001-03-09 21:24:12 +0000299static int countdirs(struct dnode **dn, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000300{
301 int i, dirs;
302
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000303 if (dn == NULL || nfiles < 1)
304 return (0);
305 dirs = 0;
306 for (i = 0; i < nfiles; i++) {
307 if (S_ISDIR(dn[i]->dstat.st_mode))
308 dirs++;
Eric Andersen11c65522000-09-07 17:24:47 +0000309 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000310 return (dirs);
Eric Andersen11c65522000-09-07 17:24:47 +0000311}
312
Eric Andersen3e6ff902001-03-09 21:24:12 +0000313static int countsubdirs(struct dnode **dn, int nfiles)
Eric Andersencf1189f2000-11-29 21:52:06 +0000314{
315 int i, subdirs;
316
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000317 if (dn == NULL || nfiles < 1)
318 return 0;
Eric Andersencf1189f2000-11-29 21:52:06 +0000319 subdirs = 0;
320 for (i = 0; i < nfiles; i++)
321 if (is_subdir(dn[i]))
322 subdirs++;
323 return subdirs;
324}
325
Eric Andersen3e6ff902001-03-09 21:24:12 +0000326static int countfiles(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000327{
328 int nfiles;
329 struct dnode *cur;
330
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000331 if (dnp == NULL)
332 return (0);
333 nfiles = 0;
334 for (cur = dnp[0]; cur->next != NULL; cur = cur->next)
335 nfiles++;
Eric Andersen11c65522000-09-07 17:24:47 +0000336 nfiles++;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000337 return (nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000338}
339
340/* get memory to hold an array of pointers */
Eric Andersen3e6ff902001-03-09 21:24:12 +0000341static struct dnode **dnalloc(int num)
Eric Andersen11c65522000-09-07 17:24:47 +0000342{
343 struct dnode **p;
344
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000345 if (num < 1)
346 return (NULL);
Eric Andersen11c65522000-09-07 17:24:47 +0000347
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000348 p = (struct dnode **) xcalloc((size_t) num,
349 (size_t) (sizeof(struct dnode *)));
350 return (p);
Eric Andersen11c65522000-09-07 17:24:47 +0000351}
352
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000353#ifdef CONFIG_FEATURE_LS_RECURSIVE
Eric Andersen3e6ff902001-03-09 21:24:12 +0000354static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000355{
356 struct dnode *cur, *next;
357
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000358 if (dnp == NULL)
359 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000360
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000361 cur = dnp[0];
Eric Andersen11c65522000-09-07 17:24:47 +0000362 while (cur != NULL) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000363 if (cur->fullname != NULL)
364 free(cur->fullname); /* free the filename */
365 next = cur->next;
366 free(cur); /* free the dnode */
367 cur = next;
Eric Andersen11c65522000-09-07 17:24:47 +0000368 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000369 free(dnp); /* free the array holding the dnode pointers */
Eric Andersen11c65522000-09-07 17:24:47 +0000370}
Eric Andersen20aab262001-07-19 22:28:02 +0000371#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000372
Eric Andersen3e6ff902001-03-09 21:24:12 +0000373static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000374{
375 int dncnt, i, d;
376 struct dnode **dnp;
377
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000378 if (dn == NULL || nfiles < 1)
379 return (NULL);
Eric Andersen11c65522000-09-07 17:24:47 +0000380
381 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000382 if (which == SPLIT_SUBDIR)
383 dncnt = countsubdirs(dn, nfiles);
384 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000385 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000386 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000387 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000388 }
Eric Andersen11c65522000-09-07 17:24:47 +0000389
390 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000391 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000392
393 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000394 for (d = i = 0; i < nfiles; i++) {
Eric Andersen11c65522000-09-07 17:24:47 +0000395 if (which == SPLIT_DIR) {
396 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000397 dnp[d++] = dn[i];
398 } /* else skip the file */
Eric Andersencf1189f2000-11-29 21:52:06 +0000399 } else if (which == SPLIT_SUBDIR) {
400 if (is_subdir(dn[i])) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000401 dnp[d++] = dn[i];
402 } /* else skip the file or dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000403 } else {
404 if (!(S_ISDIR(dn[i]->dstat.st_mode))) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000405 dnp[d++] = dn[i];
406 } /* else skip the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000407 }
408 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000409 return (dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000410}
411
412/*----------------------------------------------------------------------*/
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000413#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen3e6ff902001-03-09 21:24:12 +0000414static int sortcmp(struct dnode *d1, struct dnode *d2)
Eric Andersen11c65522000-09-07 17:24:47 +0000415{
416 int cmp, dif;
417
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000418 cmp = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000419 if (sort_opts == SORT_SIZE) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000420 dif = (int) (d1->dstat.st_size - d2->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000421 } else if (sort_opts == SORT_ATIME) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000422 dif = (int) (d1->dstat.st_atime - d2->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000423 } else if (sort_opts == SORT_CTIME) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000424 dif = (int) (d1->dstat.st_ctime - d2->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000425 } else if (sort_opts == SORT_MTIME) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000426 dif = (int) (d1->dstat.st_mtime - d2->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000427 } else if (sort_opts == SORT_DIR) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000428 dif = S_ISDIR(d1->dstat.st_mode) - S_ISDIR(d2->dstat.st_mode);
429 /* } else if (sort_opts == SORT_VERSION) { */
430 /* } else if (sort_opts == SORT_EXT) { */
431 } else { /* assume SORT_NAME */
432 dif = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000433 }
434
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000435 if (dif > 0)
436 cmp = -1;
437 if (dif < 0)
438 cmp = 1;
Eric Andersen11c65522000-09-07 17:24:47 +0000439 if (dif == 0) {
440 /* sort by name- may be a tie_breaker for time or size cmp */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000441 dif = strcmp(d1->name, d2->name);
442 if (dif > 0)
443 cmp = 1;
444 if (dif < 0)
445 cmp = -1;
Eric Andersen11c65522000-09-07 17:24:47 +0000446 }
447
448 if (sort_order == SORT_REVERSE) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000449 cmp = -1 * cmp;
Eric Andersen11c65522000-09-07 17:24:47 +0000450 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000451 return (cmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000452}
453
454/*----------------------------------------------------------------------*/
Eric Andersen3e6ff902001-03-09 21:24:12 +0000455static void shellsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000456{
457 struct dnode *temp;
458 int gap, i, j;
459
460 /* shell short the array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000461 if (dn == NULL || size < 2)
462 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000463
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000464 for (gap = size / 2; gap > 0; gap /= 2) {
465 for (i = gap; i < size; i++) {
466 for (j = i - gap; j >= 0; j -= gap) {
467 if (sortcmp(dn[j], dn[j + gap]) <= 0)
Eric Andersen11c65522000-09-07 17:24:47 +0000468 break;
469 /* they are out of order, swap them */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000470 temp = dn[j];
471 dn[j] = dn[j + gap];
472 dn[j + gap] = temp;
Eric Andersen11c65522000-09-07 17:24:47 +0000473 }
474 }
475 }
476}
477#endif
478
479/*----------------------------------------------------------------------*/
Eric Andersen3e6ff902001-03-09 21:24:12 +0000480static void showfiles(struct dnode **dn, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000481{
482 int i, ncols, nrows, row, nc;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000483
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000484#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen11c65522000-09-07 17:24:47 +0000485 int len;
486#endif
487
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000488 if (dn == NULL || nfiles < 1)
489 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000490
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000491#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen11c65522000-09-07 17:24:47 +0000492 /* find the longest file name- use that as the column width */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000493 column_width = 0;
494 for (i = 0; i < nfiles; i++) {
495 len = strlen(dn[i]->name) +
Eric Andersen11c65522000-09-07 17:24:47 +0000496 ((list_fmt & LIST_INO) ? 8 : 0) +
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000497 ((list_fmt & LIST_BLOCKS) ? 5 : 0);
498 if (column_width < len)
499 column_width = len;
Eric Andersen11c65522000-09-07 17:24:47 +0000500 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000501 ncols = (int) (terminal_width / (column_width + COLUMN_GAP));
Eric Andersenf5d5e772001-01-24 23:34:48 +0000502#else
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000503 ncols = TERMINAL_WIDTH;
Eric Andersenf5d5e772001-01-24 23:34:48 +0000504#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000505 switch (style_fmt) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000506 case STYLE_LONG: /* one record per line, extended info */
507 case STYLE_SINGLE: /* one record per line */
508 ncols = 1;
509 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000510 }
511
Eric Andersene57d54b2001-01-30 18:03:11 +0000512 if (ncols > 1) {
513 nrows = nfiles / ncols;
514 } else {
515 nrows = nfiles;
516 ncols = 1;
517 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000518 if ((nrows * ncols) < nfiles)
519 nrows++; /* round up fractionals */
Eric Andersen11c65522000-09-07 17:24:47 +0000520
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000521 if (nrows > nfiles)
522 nrows = nfiles;
523 for (row = 0; row < nrows; row++) {
524 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000525 /* reach into the array based on the column and row */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000526 i = (nc * nrows) + row; /* assume display by column */
Eric Andersen11c65522000-09-07 17:24:47 +0000527 if (disp_opts & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000528 i = (row * ncols) + nc; /* display across row */
Eric Andersen11c65522000-09-07 17:24:47 +0000529 if (i < nfiles) {
530 nexttabstop();
531 list_single(dn[i]);
Eric Andersen79565b62000-08-11 18:10:21 +0000532 }
533 }
Eric Andersena42982e2000-06-07 17:28:53 +0000534 newline();
535 }
Eric Andersen11c65522000-09-07 17:24:47 +0000536}
537
538/*----------------------------------------------------------------------*/
Eric Andersen3e6ff902001-03-09 21:24:12 +0000539static void showdirs(struct dnode **dn, int ndirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000540{
541 int i, nfiles;
542 struct dnode **subdnp;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000543
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000544#ifdef CONFIG_FEATURE_LS_RECURSIVE
Eric Andersen11c65522000-09-07 17:24:47 +0000545 int dndirs;
546 struct dnode **dnd;
547#endif
548
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000549 if (dn == NULL || ndirs < 1)
550 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000551
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000552 for (i = 0; i < ndirs; i++) {
Eric Andersen11c65522000-09-07 17:24:47 +0000553 if (disp_opts & (DISP_DIRNAME | DISP_RECURSIVE)) {
Matt Kraai12f417e2001-01-18 02:57:08 +0000554 printf("\n%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000555 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000556 subdnp = list_dir(dn[i]->fullname);
557 nfiles = countfiles(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000558 if (nfiles > 0) {
559 /* list all files at this level */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000560#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen11c65522000-09-07 17:24:47 +0000561 shellsort(subdnp, nfiles);
562#endif
563 showfiles(subdnp, nfiles);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000564#ifdef CONFIG_FEATURE_LS_RECURSIVE
Eric Andersen11c65522000-09-07 17:24:47 +0000565 if (disp_opts & DISP_RECURSIVE) {
566 /* recursive- list the sub-dirs */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000567 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
568 dndirs = countsubdirs(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000569 if (dndirs > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000570#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen11c65522000-09-07 17:24:47 +0000571 shellsort(dnd, dndirs);
Matt Kraaia5bd2682000-10-28 06:40:09 +0000572#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000573 showdirs(dnd, dndirs);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000574 free(dnd); /* free the array of dnode pointers to the dirs */
Eric Andersen11c65522000-09-07 17:24:47 +0000575 }
576 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000577 dfree(subdnp); /* free the dnodes and the fullname mem */
Eric Andersen11c65522000-09-07 17:24:47 +0000578#endif
579 }
580 }
581}
582
583/*----------------------------------------------------------------------*/
Eric Andersen3e6ff902001-03-09 21:24:12 +0000584static struct dnode **list_dir(char *path)
Eric Andersen11c65522000-09-07 17:24:47 +0000585{
586 struct dnode *dn, *cur, **dnp;
587 struct dirent *entry;
588 DIR *dir;
Eric Andersen11c65522000-09-07 17:24:47 +0000589 int i, nfiles;
590
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000591 if (path == NULL)
592 return (NULL);
Eric Andersen11c65522000-09-07 17:24:47 +0000593
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000594 dn = NULL;
595 nfiles = 0;
Eric Andersene7e1e2d2000-10-12 22:40:14 +0000596 dir = opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000597 if (dir == NULL) {
Matt Kraai1fa1ade2000-12-18 03:57:16 +0000598 perror_msg("%s", path);
Matt Kraai33fdae52000-10-13 17:59:43 +0000599 status = EXIT_FAILURE;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000600 return (NULL); /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000601 }
602 while ((entry = readdir(dir)) != NULL) {
603 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000604 if ((strcmp(entry->d_name, ".") == 0) && !(disp_opts & DISP_DOT))
Matt Kraai782ab3c2001-04-23 01:07:00 +0000605 continue;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000606 if ((strcmp(entry->d_name, "..") == 0) && !(disp_opts & DISP_DOT))
Matt Kraai782ab3c2001-04-23 01:07:00 +0000607 continue;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000608 if ((entry->d_name[0] == '.') && !(disp_opts & DISP_HIDDEN))
Matt Kraai782ab3c2001-04-23 01:07:00 +0000609 continue;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000610 cur = (struct dnode *) xmalloc(sizeof(struct dnode));
Matt Kraai782ab3c2001-04-23 01:07:00 +0000611 cur->fullname = concat_path_file(path, entry->d_name);
612 cur->name = cur->fullname +
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000613 (strlen(cur->fullname) - strlen(entry->d_name));
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000614 if (my_stat(cur))
Eric Andersen11c65522000-09-07 17:24:47 +0000615 continue;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000616 cur->next = dn;
617 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000618 nfiles++;
619 }
620 closedir(dir);
621
622 /* now that we know how many files there are
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000623 ** allocate memory for an array to hold dnode pointers
624 */
625 if (nfiles < 1)
626 return (NULL);
627 dnp = dnalloc(nfiles);
628 for (i = 0, cur = dn; i < nfiles; i++) {
629 dnp[i] = cur; /* save pointer to node in array */
630 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +0000631 }
632
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000633 return (dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000634}
635
636/*----------------------------------------------------------------------*/
Eric Andersen3e6ff902001-03-09 21:24:12 +0000637static int list_single(struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000638{
Mark Whitley8a633262001-04-30 18:17:00 +0000639 int i;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000640
Matt Kraaia1bbde72002-03-08 16:25:33 +0000641#ifdef CONFIG_FEATURE_LS_USERNAME
Eric Andersen11c65522000-09-07 17:24:47 +0000642 char scratch[BUFSIZ + 1];
Matt Kraaia1bbde72002-03-08 16:25:33 +0000643#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000644#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000645 char *filetime;
646 time_t ttime, age;
647#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000648#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
Eric Andersen11c65522000-09-07 17:24:47 +0000649 struct stat info;
650 char append;
651#endif
652
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000653 if (dn == NULL || dn->fullname == NULL)
654 return (0);
Eric Andersen11c65522000-09-07 17:24:47 +0000655
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000656#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000657 ttime = dn->dstat.st_mtime; /* the default time */
658 if (time_fmt & TIME_ACCESS)
659 ttime = dn->dstat.st_atime;
660 if (time_fmt & TIME_CHANGE)
661 ttime = dn->dstat.st_ctime;
662 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000663#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000664#ifdef CONFIG_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000665 append = append_char(dn->dstat.st_mode);
666#endif
667
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000668 for (i = 0; i <= 31; i++) {
669 switch (list_fmt & (1 << i)) {
670 case LIST_INO:
671 printf("%7ld ", (long int) dn->dstat.st_ino);
672 column += 8;
673 break;
674 case LIST_BLOCKS:
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000675#ifdef CONFIG_FEATURE_HUMAN_READABLE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000676 fprintf(stdout, "%6s ",
677 make_human_readable_str(dn->dstat.st_blocks >> 1,
678 KILOBYTE,
679 (ls_disp_hr ==
680 TRUE) ? 0 : KILOBYTE));
Richard June6d0921c2001-01-22 22:35:38 +0000681#else
Eric Andersen8a2e56c2000-09-21 02:23:30 +0000682#if _FILE_OFFSET_BITS == 64
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000683 printf("%4lld ", dn->dstat.st_blocks >> 1);
Eric Andersen8a2e56c2000-09-21 02:23:30 +0000684#else
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000685 printf("%4ld ", dn->dstat.st_blocks >> 1);
Eric Andersen8a2e56c2000-09-21 02:23:30 +0000686#endif
Richard June6d0921c2001-01-22 22:35:38 +0000687#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000688 column += 5;
689 break;
690 case LIST_MODEBITS:
691 printf("%-10s ", (char *) mode_string(dn->dstat.st_mode));
692 column += 10;
693 break;
694 case LIST_NLINKS:
695 printf("%4ld ", (long) dn->dstat.st_nlink);
696 column += 10;
697 break;
698 case LIST_ID_NAME:
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000699#ifdef CONFIG_FEATURE_LS_USERNAME
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000700 my_getpwuid(scratch, dn->dstat.st_uid);
701 printf("%-8.8s ", scratch);
702 my_getgrgid(scratch, dn->dstat.st_gid);
703 printf("%-8.8s", scratch);
704 column += 17;
705 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000706#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000707 case LIST_ID_NUMERIC:
708 printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
709 column += 17;
710 break;
711 case LIST_SIZE:
712 case LIST_DEV:
713 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
714 printf("%4d, %3d ", (int) MAJOR(dn->dstat.st_rdev),
715 (int) MINOR(dn->dstat.st_rdev));
716 } else {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000717#ifdef CONFIG_FEATURE_HUMAN_READABLE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000718 if (ls_disp_hr == TRUE) {
719 fprintf(stdout, "%8s ",
720 make_human_readable_str(dn->dstat.st_size, 1, 0));
721 } else
722#endif
723 {
Eric Andersen8a2e56c2000-09-21 02:23:30 +0000724#if _FILE_OFFSET_BITS == 64
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000725 printf("%9lld ", (long long) dn->dstat.st_size);
Eric Andersen8a2e56c2000-09-21 02:23:30 +0000726#else
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000727 printf("%9ld ", dn->dstat.st_size);
Eric Andersen8a2e56c2000-09-21 02:23:30 +0000728#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000729 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000730 }
731 column += 10;
732 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000733#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000734 case LIST_FULLTIME:
735 case LIST_DATE_TIME:
736 if (list_fmt & LIST_FULLTIME) {
737 printf("%24.24s ", filetime);
738 column += 25;
Eric Andersen11c65522000-09-07 17:24:47 +0000739 break;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000740 }
741 age = time(NULL) - ttime;
742 printf("%6.6s ", filetime + 4);
743 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
744 /* hh:mm if less than 6 months old */
745 printf("%5.5s ", filetime + 11);
746 } else {
747 printf(" %4.4s ", filetime + 20);
748 }
749 column += 13;
750 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000751#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000752 case LIST_FILENAME:
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000753#ifdef CONFIG_FEATURE_LS_COLOR
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000754 errno = 0;
755 if (show_color && !lstat(dn->fullname, &info)) {
756 printf("\033[%d;%dm", bgcolor(info.st_mode),
757 fgcolor(info.st_mode));
758 }
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000759#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000760 printf("%s", dn->name);
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000761#ifdef CONFIG_FEATURE_LS_COLOR
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000762 if (show_color) {
763 printf("\033[0m");
764 }
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000765#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000766 column += strlen(dn->name);
767 break;
768 case LIST_SYMLINK:
769 if (S_ISLNK(dn->dstat.st_mode)) {
770 char *lpath = xreadlink(dn->fullname);
771
772 if (lpath) {
773 printf(" -> ");
774#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
775 if (!stat(dn->fullname, &info)) {
776 append = append_char(info.st_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000777 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000778#endif
779#ifdef CONFIG_FEATURE_LS_COLOR
780 if (show_color) {
781 errno = 0;
782 printf("\033[%d;%dm", bgcolor(info.st_mode),
783 fgcolor(info.st_mode));
784 }
785#endif
786 printf("%s", lpath);
787#ifdef CONFIG_FEATURE_LS_COLOR
788 if (show_color) {
789 printf("\033[0m");
790 }
791#endif
792 column += strlen(lpath) + 4;
793 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000794 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000795 }
796 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000797#ifdef CONFIG_FEATURE_LS_FILETYPES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000798 case LIST_FILETYPE:
799 if (append != '\0') {
800 printf("%1c", append);
801 column++;
802 }
803 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000804#endif
805 }
806 }
807
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000808 return (0);
Eric Andersen11c65522000-09-07 17:24:47 +0000809}
810
811/*----------------------------------------------------------------------*/
812extern int ls_main(int argc, char **argv)
813{
814 struct dnode **dnf, **dnd;
815 int dnfiles, dndirs;
816 struct dnode *dn, *cur, **dnp;
817 int i, nfiles;
818 int opt;
819 int oi, ac;
820 char **av;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000821
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000822#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen5307eca2001-01-26 01:52:43 +0000823 struct winsize win = { 0, 0, 0, 0 };
824#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000825
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000826 disp_opts = DISP_NORMAL;
827 style_fmt = STYLE_AUTO;
828 list_fmt = LIST_SHORT;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000829#ifdef CONFIG_FEATURE_LS_SORTFILES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000830 sort_opts = SORT_NAME;
831 sort_order = SORT_FORWARD;
Eric Andersen11c65522000-09-07 17:24:47 +0000832#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000833#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000834 time_fmt = TIME_MOD;
Eric Andersen11c65522000-09-07 17:24:47 +0000835#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000836#ifdef CONFIG_FEATURE_AUTOWIDTH
Mark Whitley9b300d02001-02-01 19:39:43 +0000837 ioctl(fileno(stdout), TIOCGWINSZ, &win);
838 if (win.ws_row > 4)
839 column_width = win.ws_row - 2;
840 if (win.ws_col > 0)
841 terminal_width = win.ws_col - 1;
Eric Andersen5307eca2001-01-26 01:52:43 +0000842#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000843 nfiles = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000844
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000845#ifdef CONFIG_FEATURE_LS_COLOR
846 if (isatty(fileno(stdout)))
847 show_color = 1;
848#endif
849
Eric Andersen11c65522000-09-07 17:24:47 +0000850 /* process options */
851 while ((opt = getopt(argc, argv, "1AaCdgilnsx"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000852#ifdef CONFIG_FEATURE_AUTOWIDTH
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000853 "T:w:"
Eric Andersen11c65522000-09-07 17:24:47 +0000854#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000855#ifdef CONFIG_FEATURE_LS_FILETYPES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000856 "Fp"
Eric Andersen11c65522000-09-07 17:24:47 +0000857#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000858#ifdef CONFIG_FEATURE_LS_RECURSIVE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000859 "R"
Eric Andersen11c65522000-09-07 17:24:47 +0000860#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000861#ifdef CONFIG_FEATURE_LS_SORTFILES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000862 "rSvX"
Eric Andersen11c65522000-09-07 17:24:47 +0000863#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000864#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000865 "cetu"
Eric Andersen11c65522000-09-07 17:24:47 +0000866#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000867#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000868 "L"
Matt Kraaia2f2a8f2000-09-22 03:11:47 +0000869#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000870#ifdef CONFIG_FEATURE_HUMAN_READABLE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000871 "h"
Richard June6d0921c2001-01-22 22:35:38 +0000872#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000873 "k")) > 0) {
Eric Andersen11c65522000-09-07 17:24:47 +0000874 switch (opt) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000875 case '1':
876 style_fmt = STYLE_SINGLE;
877 break;
878 case 'A':
879 disp_opts |= DISP_HIDDEN;
880 break;
881 case 'a':
882 disp_opts |= DISP_HIDDEN | DISP_DOT;
883 break;
884 case 'C':
885 style_fmt = STYLE_COLUMNS;
886 break;
887 case 'd':
888 disp_opts |= DISP_NOLIST;
889 break;
890 case 'g': /* ignore -- for ftp servers */
891 break;
892 case 'i':
893 list_fmt |= LIST_INO;
894 break;
895 case 'l':
896 style_fmt = STYLE_LONG;
897 list_fmt |= LIST_LONG;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000898#ifdef CONFIG_FEATURE_HUMAN_READABLE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000899 ls_disp_hr = FALSE;
Richard June6d0921c2001-01-22 22:35:38 +0000900#endif
901 break;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000902 case 'n':
903 list_fmt |= LIST_ID_NUMERIC;
904 break;
905 case 's':
906 list_fmt |= LIST_BLOCKS;
907 break;
908 case 'x':
909 disp_opts = DISP_ROWS;
910 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000911#ifdef CONFIG_FEATURE_LS_FILETYPES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000912 case 'F':
913 list_fmt |= LIST_FILETYPE | LIST_EXEC;
914 break;
915 case 'p':
916 list_fmt |= LIST_FILETYPE;
917 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000918#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000919#ifdef CONFIG_FEATURE_LS_RECURSIVE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000920 case 'R':
921 disp_opts |= DISP_RECURSIVE;
922 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000923#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000924#ifdef CONFIG_FEATURE_LS_SORTFILES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000925 case 'r':
926 sort_order |= SORT_REVERSE;
927 break;
928 case 'S':
929 sort_opts = SORT_SIZE;
930 break;
931 case 'v':
932 sort_opts = SORT_VERSION;
933 break;
934 case 'X':
935 sort_opts = SORT_EXT;
936 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000937#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000938#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000939 case 'e':
940 list_fmt |= LIST_FULLTIME;
941 break;
942 case 'c':
943 time_fmt = TIME_CHANGE;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000944#ifdef CONFIG_FEATURE_LS_SORTFILES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000945 sort_opts = SORT_CTIME;
Matt Kraaia5bd2682000-10-28 06:40:09 +0000946#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000947 break;
948 case 'u':
949 time_fmt = TIME_ACCESS;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000950#ifdef CONFIG_FEATURE_LS_SORTFILES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000951 sort_opts = SORT_ATIME;
Matt Kraaia5bd2682000-10-28 06:40:09 +0000952#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000953 break;
954 case 't':
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000955#ifdef CONFIG_FEATURE_LS_SORTFILES
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000956 sort_opts = SORT_MTIME;
Matt Kraaia5bd2682000-10-28 06:40:09 +0000957#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000958 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000959#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000960#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000961 case 'L':
962 follow_links = TRUE;
963 break;
Matt Kraaia2f2a8f2000-09-22 03:11:47 +0000964#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000965#ifdef CONFIG_FEATURE_AUTOWIDTH
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000966 case 'T':
967 tabstops = atoi(optarg);
968 break;
969 case 'w':
970 terminal_width = atoi(optarg);
971 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000972#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000973#ifdef CONFIG_FEATURE_HUMAN_READABLE
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000974 case 'h':
975 ls_disp_hr = TRUE;
976 break;
Richard June6d0921c2001-01-22 22:35:38 +0000977#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000978 case 'k':
979 break;
980 default:
981 goto print_usage_message;
Eric Andersen11c65522000-09-07 17:24:47 +0000982 }
983 }
984
985 /* sort out which command line options take precedence */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000986#ifdef CONFIG_FEATURE_LS_RECURSIVE
Eric Andersen11c65522000-09-07 17:24:47 +0000987 if (disp_opts & DISP_NOLIST)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000988 disp_opts &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000989#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000990#if defined (CONFIG_FEATURE_LS_TIMESTAMPS) && defined (CONFIG_FEATURE_LS_SORTFILES)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000991 if (time_fmt & TIME_CHANGE)
992 sort_opts = SORT_CTIME;
993 if (time_fmt & TIME_ACCESS)
994 sort_opts = SORT_ATIME;
Eric Andersen11c65522000-09-07 17:24:47 +0000995#endif
996 if (style_fmt != STYLE_LONG)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000997 list_fmt &= ~LIST_ID_NUMERIC; /* numeric uid only for long list */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000998#ifdef CONFIG_FEATURE_LS_USERNAME
Eric Andersen11c65522000-09-07 17:24:47 +0000999 if (style_fmt == STYLE_LONG && (list_fmt & LIST_ID_NUMERIC))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001000 list_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersen11c65522000-09-07 17:24:47 +00001001#endif
1002
1003 /* choose a display format */
1004 if (style_fmt == STYLE_AUTO)
1005 style_fmt = isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE;
1006
1007 /*
1008 * when there are no cmd line args we have to supply a default "." arg.
1009 * we will create a second argv array, "av" that will hold either
1010 * our created "." arg, or the real cmd line args. The av array
1011 * just holds the pointers- we don't move the date the pointers
1012 * point to.
1013 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001014 ac = argc - optind; /* how many cmd line args are left */
Eric Andersen11c65522000-09-07 17:24:47 +00001015 if (ac < 1) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001016 av = (char **) xcalloc((size_t) 1, (size_t) (sizeof(char *)));
1017 av[0] = xstrdup(".");
1018 ac = 1;
Eric Andersen11c65522000-09-07 17:24:47 +00001019 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001020 av = (char **) xcalloc((size_t) ac, (size_t) (sizeof(char *)));
1021 for (oi = 0; oi < ac; oi++) {
1022 av[oi] = argv[optind++]; /* copy pointer to real cmd line arg */
Eric Andersen11c65522000-09-07 17:24:47 +00001023 }
1024 }
1025
1026 /* now, everything is in the av array */
1027 if (ac > 1)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001028 disp_opts |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001029
1030 /* stuff the command line file names into an dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001031 dn = NULL;
1032 for (oi = 0; oi < ac; oi++) {
1033 cur = (struct dnode *) xmalloc(sizeof(struct dnode));
1034 cur->fullname = xstrdup(av[oi]);
1035 cur->name = cur->fullname;
Matt Kraai9a6e67c2000-10-13 18:03:21 +00001036 if (my_stat(cur))
Eric Andersen11c65522000-09-07 17:24:47 +00001037 continue;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001038 cur->next = dn;
1039 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001040 nfiles++;
1041 }
1042
1043 /* now that we know how many files there are
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001044 ** allocate memory for an array to hold dnode pointers
1045 */
1046 dnp = dnalloc(nfiles);
1047 for (i = 0, cur = dn; i < nfiles; i++) {
1048 dnp[i] = cur; /* save pointer to node in array */
1049 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +00001050 }
1051
1052
1053 if (disp_opts & DISP_NOLIST) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001054#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen11c65522000-09-07 17:24:47 +00001055 shellsort(dnp, nfiles);
1056#endif
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001057 if (nfiles > 0)
1058 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001059 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001060 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1061 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1062 dndirs = countdirs(dnp, nfiles);
1063 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001064 if (dnfiles > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001065#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen11c65522000-09-07 17:24:47 +00001066 shellsort(dnf, dnfiles);
1067#endif
1068 showfiles(dnf, dnfiles);
1069 }
1070 if (dndirs > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001071#ifdef CONFIG_FEATURE_LS_SORTFILES
Eric Andersen11c65522000-09-07 17:24:47 +00001072 shellsort(dnd, dndirs);
1073#endif
1074 showdirs(dnd, dndirs);
1075 }
1076 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001077 return (status);
Eric Andersencc8ed391999-10-05 16:24:54 +00001078
Erik Andersene49d5ec2000-02-08 19:58:47 +00001079 print_usage_message:
Eric Andersen67991cf2001-02-14 21:23:06 +00001080 show_usage();
Eric Andersencc8ed391999-10-05 16:24:54 +00001081}