620 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			620 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* vi: set sw=4 ts=4: */
 | 
						|
/*
 | 
						|
 * tiny-ls.c version 0.1.0: A minimalist 'ls'
 | 
						|
 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
 | 
						|
 * 
 | 
						|
 *  This program is free software; you can redistribute it and/or modify
 | 
						|
 *  it under the terms of the GNU General Public License as published by
 | 
						|
 *  the Free Software Foundation; either version 2 of the License, or
 | 
						|
 *  (at your option) any later version.
 | 
						|
 *
 | 
						|
 *  This program is distributed in the hope that it will be useful,
 | 
						|
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 *  GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 *  You should have received a copy of the GNU General Public License
 | 
						|
 *  along with this program; if not, write to the Free Software
 | 
						|
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * To achieve a small memory footprint, this version of 'ls' doesn't do any
 | 
						|
 * file sorting, and only has the most essential command line switches
 | 
						|
 * (i.e. the ones I couldn't live without :-) All features which involve
 | 
						|
 * linking in substantial chunks of libc can be disabled.
 | 
						|
 *
 | 
						|
 * Although I don't really want to add new features to this program to
 | 
						|
 * keep it small, I *am* interested to receive bug fixes and ways to make
 | 
						|
 * it more portable.
 | 
						|
 *
 | 
						|
 * KNOWN BUGS:
 | 
						|
 * 1. ls -l of a directory doesn't give "total <blocks>" header
 | 
						|
 * 2. ls of a symlink to a directory doesn't list directory contents
 | 
						|
 * 3. hidden files can make column width too large
 | 
						|
 *
 | 
						|
 * NON-OPTIMAL BEHAVIOUR:
 | 
						|
 * 1. autowidth reads directories twice
 | 
						|
 * 2. if you do a short directory listing without filetype characters
 | 
						|
 *    appended, there's no need to stat each one
 | 
						|
 * PORTABILITY:
 | 
						|
 * 1. requires lstat (BSD) - how do you do it without?
 | 
						|
 */
 | 
						|
 | 
						|
#define TERMINAL_WIDTH	80		/* use 79 if your terminal has linefold bug */
 | 
						|
#define	COLUMN_WIDTH	14		/* default if AUTOWIDTH not defined */
 | 
						|
#define COLUMN_GAP	2			/* includes the file type char, if present */
 | 
						|
#define HAS_REWINDDIR
 | 
						|
 | 
						|
/************************************************************************/
 | 
						|
 | 
						|
#include "internal.h"
 | 
						|
# include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <stdio.h>
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
#include <time.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#define TYPEINDEX(mode)	(((mode) >> 12) & 0x0f)
 | 
						|
#define TYPECHAR(mode)	("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
#define APPCHAR(mode)	("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
 | 
						|
#endif
 | 
						|
 | 
						|
#define FMT_AUTO	0
 | 
						|
#define FMT_LONG	1			/* one record per line, extended info */
 | 
						|
#define FMT_SINGLE	2			/* one record per line */
 | 
						|
#define FMT_ROWS	3			/* print across rows */
 | 
						|
#define FMT_COLUMNS	3			/* fill columns (same, since we don't sort) */
 | 
						|
 | 
						|
#define TIME_MOD	0
 | 
						|
#define TIME_CHANGE	1
 | 
						|
#define TIME_ACCESS	2
 | 
						|
 | 
						|
#define DISP_FTYPE	1			/* show character for file type */
 | 
						|
#define DISP_EXEC	2			/* show '*' if regular executable file */
 | 
						|
#define DISP_HIDDEN	4			/* show files starting . (except . and ..) */
 | 
						|
#define DISP_DOT	8			/* show . and .. */
 | 
						|
#define DISP_NUMERIC	16		/* numeric uid and gid */
 | 
						|
#define DISP_FULLTIME	32		/* show extended time display */
 | 
						|
#define DIR_NOLIST		64		/* show directory as itself, not contents */
 | 
						|
#define DISP_DIRNAME	128		/* show directory name (for internal use) */
 | 
						|
#define DISP_RECURSIVE	256		/* Do a recursive listing */
 | 
						|
 | 
						|
#ifndef MAJOR
 | 
						|
#define MAJOR(dev) (((dev)>>8)&0xff)
 | 
						|
#define MINOR(dev) ((dev)&0xff)
 | 
						|
#endif
 | 
						|
 | 
						|
static unsigned char display_fmt = FMT_AUTO;
 | 
						|
static unsigned short opts = 0;
 | 
						|
static unsigned short column = 0;
 | 
						|
 | 
						|
#ifdef BB_FEATURE_AUTOWIDTH
 | 
						|
static unsigned short terminal_width = 0;
 | 
						|
static unsigned short column_width = 0;
 | 
						|
static unsigned short toplevel_column_width = 0;
 | 
						|
#else
 | 
						|
#define terminal_width	TERMINAL_WIDTH
 | 
						|
#define column_width	COLUMN_WIDTH
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
static unsigned char time_fmt = TIME_MOD;
 | 
						|
#endif
 | 
						|
 | 
						|
#define wr(data,len) fwrite(data, 1, len, stdout)
 | 
						|
 | 
						|
static void writenum(long val, short minwidth)
 | 
						|
{
 | 
						|
	char scratch[128];
 | 
						|
 | 
						|
	char *p = scratch + sizeof(scratch);
 | 
						|
	short len = 0;
 | 
						|
	short neg = (val < 0);
 | 
						|
 | 
						|
	if (neg)
 | 
						|
		val = -val;
 | 
						|
	do
 | 
						|
		*--p = (val % 10) + '0', len++, val /= 10;
 | 
						|
	while (val);
 | 
						|
	if (neg)
 | 
						|
		*--p = '-', len++;
 | 
						|
	while (len < minwidth)
 | 
						|
		*--p = ' ', len++;
 | 
						|
	wr(p, len);
 | 
						|
	column += len;
 | 
						|
}
 | 
						|
 | 
						|
static void newline(void)
 | 
						|
{
 | 
						|
	if (column > 0) {
 | 
						|
		wr("\n", 1);
 | 
						|
		column = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void tab(short col)
 | 
						|
{
 | 
						|
	static const char spaces[] = "                ";
 | 
						|
 | 
						|
#define nspaces ((sizeof spaces)-1)	/* null terminator! */
 | 
						|
 | 
						|
	short n = col - column;
 | 
						|
 | 
						|
	if (n > 0) {
 | 
						|
		column = col;
 | 
						|
		while (n > nspaces) {
 | 
						|
			wr(spaces, nspaces);
 | 
						|
			n -= nspaces;
 | 
						|
		}
 | 
						|
		/* must be 1...(sizeof spaces) left */
 | 
						|
		wr(spaces, n);
 | 
						|
	}
 | 
						|
#undef nspaces
 | 
						|
}
 | 
						|
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
static char append_char(mode_t mode)
 | 
						|
{
 | 
						|
	if (!(opts & DISP_FTYPE))
 | 
						|
		return '\0';
 | 
						|
	if ((opts & DISP_EXEC) && S_ISREG(mode)
 | 
						|
		&& (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*';
 | 
						|
	return APPCHAR(mode);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
 **
 | 
						|
 ** Display a file or directory as a single item
 | 
						|
 ** (in either long or short format)
 | 
						|
 **
 | 
						|
 **/
 | 
						|
 | 
						|
static void list_single(const char *name, struct stat *info,
 | 
						|
						const char *fullname)
 | 
						|
{
 | 
						|
	char scratch[BUFSIZ + 1];
 | 
						|
	short len = strlen(name);
 | 
						|
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
	char append = append_char(info->st_mode);
 | 
						|
#endif
 | 
						|
 | 
						|
	if (display_fmt == FMT_LONG) {
 | 
						|
		mode_t mode = info->st_mode;
 | 
						|
 | 
						|
		newline();
 | 
						|
		wr(modeString(mode), 10);
 | 
						|
		column = 10;
 | 
						|
		writenum((long) info->st_nlink, (short) 5);
 | 
						|
		fputs(" ", stdout);
 | 
						|
#ifdef BB_FEATURE_LS_USERNAME
 | 
						|
		if (!(opts & DISP_NUMERIC)) {
 | 
						|
			memset(scratch, 0, sizeof(scratch));
 | 
						|
			my_getpwuid(scratch, info->st_uid);
 | 
						|
			if (*scratch) {
 | 
						|
				fputs(scratch, stdout);
 | 
						|
				if (strlen(scratch) <= 8)
 | 
						|
					wr("          ", 9 - strlen(scratch));
 | 
						|
			} else {
 | 
						|
				writenum((long) info->st_uid, (short) 8);
 | 
						|
				fputs(" ", stdout);
 | 
						|
			}
 | 
						|
		} else
 | 
						|
#endif
 | 
						|
		{
 | 
						|
			writenum((long) info->st_uid, (short) 8);
 | 
						|
			fputs(" ", stdout);
 | 
						|
		}
 | 
						|
#ifdef BB_FEATURE_LS_USERNAME
 | 
						|
		if (!(opts & DISP_NUMERIC)) {
 | 
						|
			memset(scratch, 0, sizeof(scratch));
 | 
						|
			my_getgrgid(scratch, info->st_gid);
 | 
						|
			if (*scratch) {
 | 
						|
				fputs(scratch, stdout);
 | 
						|
				if (strlen(scratch) <= 8)
 | 
						|
					wr("         ", 8 - strlen(scratch));
 | 
						|
			} else
 | 
						|
				writenum((long) info->st_gid, (short) 8);
 | 
						|
		} else
 | 
						|
#endif
 | 
						|
			writenum((long) info->st_gid, (short) 8);
 | 
						|
		//tab(26);
 | 
						|
		if (S_ISBLK(mode) || S_ISCHR(mode)) {
 | 
						|
			writenum((long) MAJOR(info->st_rdev), (short) 3);
 | 
						|
			fputs(", ", stdout);
 | 
						|
			writenum((long) MINOR(info->st_rdev), (short) 3);
 | 
						|
		} else
 | 
						|
			writenum((long) info->st_size, (short) 8);
 | 
						|
		fputs(" ", stdout);
 | 
						|
		//tab(32);
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
		{
 | 
						|
			time_t cal;
 | 
						|
			char *string;
 | 
						|
 | 
						|
			switch (time_fmt) {
 | 
						|
			case TIME_CHANGE:
 | 
						|
				cal = info->st_ctime;
 | 
						|
				break;
 | 
						|
			case TIME_ACCESS:
 | 
						|
				cal = info->st_atime;
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				cal = info->st_mtime;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			string = ctime(&cal);
 | 
						|
			if (opts & DISP_FULLTIME)
 | 
						|
				wr(string, 24);
 | 
						|
			else {
 | 
						|
				time_t age = time(NULL) - cal;
 | 
						|
 | 
						|
				wr(string + 4, 7);	/* mmm_dd_ */
 | 
						|
				if (age < 3600L * 24 * 365 / 2 && age > -15 * 60)
 | 
						|
					/* hh:mm if less than 6 months old */
 | 
						|
					wr(string + 11, 5);
 | 
						|
				else
 | 
						|
					/* _yyyy otherwise */
 | 
						|
					wr(string + 19, 5);
 | 
						|
			}
 | 
						|
			wr(" ", 1);
 | 
						|
		}
 | 
						|
#else
 | 
						|
		fputs("--- -- ----- ", stdout);
 | 
						|
#endif
 | 
						|
		wr(name, len);
 | 
						|
		if (S_ISLNK(mode)) {
 | 
						|
			wr(" -> ", 4);
 | 
						|
			len = readlink(fullname, scratch, sizeof scratch);
 | 
						|
			if (len > 0)
 | 
						|
				fwrite(scratch, 1, len, stdout);
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
			/* show type of destination */
 | 
						|
			if (opts & DISP_FTYPE) {
 | 
						|
				if (!stat(fullname, info)) {
 | 
						|
					append = append_char(info->st_mode);
 | 
						|
					if (append)
 | 
						|
						fputc(append, stdout);
 | 
						|
				}
 | 
						|
			}
 | 
						|
#endif
 | 
						|
		}
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
		else if (append)
 | 
						|
			wr(&append, 1);
 | 
						|
#endif
 | 
						|
	} else {
 | 
						|
		static short nexttab = 0;
 | 
						|
 | 
						|
		/* sort out column alignment */
 | 
						|
		if (column == 0);		/* nothing to do */
 | 
						|
		else if (display_fmt == FMT_SINGLE)
 | 
						|
			newline();
 | 
						|
		else {
 | 
						|
			if (nexttab + column_width > terminal_width
 | 
						|
#ifndef BB_FEATURE_AUTOWIDTH
 | 
						|
				|| nexttab + len >= terminal_width
 | 
						|
#endif
 | 
						|
				)
 | 
						|
				newline();
 | 
						|
			else
 | 
						|
				tab(nexttab);
 | 
						|
		}
 | 
						|
		/* work out where next column starts */
 | 
						|
#ifdef BB_FEATURE_AUTOWIDTH
 | 
						|
		/* we know the calculated width is big enough */
 | 
						|
		nexttab = column + column_width + COLUMN_GAP;
 | 
						|
#else
 | 
						|
		/* might cover more than one fixed-width column */
 | 
						|
		nexttab = column;
 | 
						|
		do
 | 
						|
			nexttab += column_width + COLUMN_GAP;
 | 
						|
		while (nexttab < (column + len + COLUMN_GAP));
 | 
						|
#endif
 | 
						|
		/* now write the data */
 | 
						|
		wr(name, len);
 | 
						|
		column = column + len;
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
		if (append)
 | 
						|
			wr(&append, 1), column++;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 **
 | 
						|
 ** List the given file or directory, expanding a directory
 | 
						|
 ** to show its contents if required
 | 
						|
 **
 | 
						|
 **/
 | 
						|
 | 
						|
static int list_item(const char *name)
 | 
						|
{
 | 
						|
	struct stat info;
 | 
						|
	DIR *dir;
 | 
						|
	struct dirent *entry;
 | 
						|
	char fullname[BUFSIZ + 1], *fnend;
 | 
						|
 | 
						|
	if (lstat(name, &info))
 | 
						|
		goto listerr;
 | 
						|
 | 
						|
	if (!S_ISDIR(info.st_mode) || (opts & DIR_NOLIST)) {
 | 
						|
#ifdef BB_FEATURE_AUTOWIDTH
 | 
						|
		column_width = toplevel_column_width;
 | 
						|
#endif
 | 
						|
		list_single(name, &info, name);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Otherwise, it's a directory we want to list the contents of */
 | 
						|
 | 
						|
	if (opts & DISP_DIRNAME) {	/* identify the directory */
 | 
						|
		if (column)
 | 
						|
			wr("\n\n", 2), column = 0;
 | 
						|
		wr(name, strlen(name));
 | 
						|
		wr(":\n", 2);
 | 
						|
	}
 | 
						|
 | 
						|
	dir = opendir(name);
 | 
						|
	if (!dir)
 | 
						|
		goto listerr;
 | 
						|
#ifdef BB_FEATURE_AUTOWIDTH
 | 
						|
	column_width = 0;
 | 
						|
	while ((entry = readdir(dir)) != NULL) {
 | 
						|
		short w = strlen(entry->d_name);
 | 
						|
 | 
						|
		if (column_width < w)
 | 
						|
			column_width = w;
 | 
						|
	}
 | 
						|
#ifdef HAS_REWINDDIR
 | 
						|
	rewinddir(dir);
 | 
						|
#else
 | 
						|
	closedir(dir);
 | 
						|
	dir = opendir(name);
 | 
						|
	if (!dir)
 | 
						|
		goto listerr;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
	/* List the contents */
 | 
						|
 | 
						|
	strcpy(fullname, name);		/* *** ignore '.' by itself */
 | 
						|
	fnend = fullname + strlen(fullname);
 | 
						|
	if (fnend[-1] != '/')
 | 
						|
		*fnend++ = '/';
 | 
						|
 | 
						|
	while ((entry = readdir(dir)) != NULL) {
 | 
						|
		const char *en = entry->d_name;
 | 
						|
 | 
						|
		if (en[0] == '.') {
 | 
						|
			if (!en[1] || (en[1] == '.' && !en[2])) {	/* . or .. */
 | 
						|
				if (!(opts & DISP_DOT))
 | 
						|
					continue;
 | 
						|
			} else if (!(opts & DISP_HIDDEN))
 | 
						|
				continue;
 | 
						|
		}
 | 
						|
		/* FIXME: avoid stat if not required */
 | 
						|
		strcpy(fnend, entry->d_name);
 | 
						|
		if (lstat(fullname, &info))
 | 
						|
			goto direrr;		/* (shouldn't fail) */
 | 
						|
		list_single(entry->d_name, &info, fullname);
 | 
						|
	}
 | 
						|
	closedir(dir);
 | 
						|
 | 
						|
	if (opts & DISP_DIRNAME) {      /* separate the directory */
 | 
						|
		if (column) {
 | 
						|
			wr("\n", 1);
 | 
						|
		}
 | 
						|
		wr("\n", 1);
 | 
						|
		column = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
  direrr:
 | 
						|
	closedir(dir);
 | 
						|
  listerr:
 | 
						|
	newline();
 | 
						|
	perror(name);
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static const char ls_usage[] = "ls [-1a"
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
	"c"
 | 
						|
#endif
 | 
						|
	"d"
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
	"e"
 | 
						|
#endif
 | 
						|
	"ln"
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
	"p"
 | 
						|
#endif
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
	"u"
 | 
						|
#endif
 | 
						|
	"xAC"
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
	"F"
 | 
						|
#endif
 | 
						|
#ifdef BB_FEATURE_LS_RECURSIVE
 | 
						|
	"R"
 | 
						|
#endif
 | 
						|
	"] [filenames...]\n"
 | 
						|
#ifndef BB_FEATURE_TRIVIAL_HELP
 | 
						|
	"\nList directory contents\n\n"
 | 
						|
	"Options:\n"
 | 
						|
	"\t-a\tdo not hide entries starting with .\n"
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
	"\t-c\twith -l: show ctime (the time of last\n"
 | 
						|
    "\t\tmodification of file status information)\n"
 | 
						|
#endif
 | 
						|
	"\t-d\tlist directory entries instead of contents\n"
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
	"\t-e\tlist both full date and full time\n"
 | 
						|
#endif
 | 
						|
	"\t-l\tuse a long listing format\n"
 | 
						|
	"\t-n\tlist numeric UIDs and GIDs instead of names\n"
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
	"\t-p\tappend indicator (one of /=@|) to entries\n"
 | 
						|
#endif
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
	"\t-u\twith -l: show access time (the time of last\n"
 | 
						|
	"\t\taccess of the file)\n"
 | 
						|
#endif
 | 
						|
	"\t-x\tlist entries by lines instead of by columns\n"
 | 
						|
	"\t-A\tdo not list implied . and ..\n"
 | 
						|
	"\t-C\tlist entries by columns\n"
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
	"\t-F\tappend indicator (one of */=@|) to entries\n"
 | 
						|
#endif
 | 
						|
#ifdef BB_FEATURE_LS_RECURSIVE
 | 
						|
	"\t-R\tlist subdirectories recursively\n"
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
	;
 | 
						|
 | 
						|
 | 
						|
#ifdef BB_FEATURE_LS_RECURSIVE
 | 
						|
static int dirAction(const char *fileName, struct stat *statbuf, void* junk)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	fprintf(stdout, "\n%s:\n", fileName);
 | 
						|
	i = list_item(fileName);
 | 
						|
	newline();
 | 
						|
	return (i);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
extern int ls_main(int argc, char **argv)
 | 
						|
{
 | 
						|
	int argi = 1, i;
 | 
						|
 | 
						|
	/* process options */
 | 
						|
	while (argi < argc && argv[argi][0] == '-') {
 | 
						|
		const char *p = &argv[argi][1];
 | 
						|
 | 
						|
		if (!*p)
 | 
						|
			goto print_usage_message;	/* "-" by itself not allowed */
 | 
						|
		if (*p == '-') {
 | 
						|
			if (!p[1]) {		/* "--" forces end of options */
 | 
						|
				argi++;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			/* it's a long option name - we don't support them */
 | 
						|
			goto print_usage_message;
 | 
						|
		}
 | 
						|
 | 
						|
		while (*p)
 | 
						|
			switch (*p++) {
 | 
						|
			case 'l':
 | 
						|
				display_fmt = FMT_LONG;
 | 
						|
				break;
 | 
						|
			case '1':
 | 
						|
				display_fmt = FMT_SINGLE;
 | 
						|
				break;
 | 
						|
			case 'x':
 | 
						|
				display_fmt = FMT_ROWS;
 | 
						|
				break;
 | 
						|
			case 'C':
 | 
						|
				display_fmt = FMT_COLUMNS;
 | 
						|
				break;
 | 
						|
#ifdef BB_FEATURE_LS_FILETYPES
 | 
						|
			case 'p':
 | 
						|
				opts |= DISP_FTYPE;
 | 
						|
				break;
 | 
						|
			case 'F':
 | 
						|
				opts |= DISP_FTYPE | DISP_EXEC;
 | 
						|
				break;
 | 
						|
#endif
 | 
						|
			case 'A':
 | 
						|
				opts |= DISP_HIDDEN;
 | 
						|
				break;
 | 
						|
			case 'a':
 | 
						|
				opts |= DISP_HIDDEN | DISP_DOT;
 | 
						|
				break;
 | 
						|
			case 'n':
 | 
						|
				opts |= DISP_NUMERIC;
 | 
						|
				break;
 | 
						|
			case 'd':
 | 
						|
				opts |= DIR_NOLIST;
 | 
						|
				break;
 | 
						|
#ifdef BB_FEATURE_LS_TIMESTAMPS
 | 
						|
			case 'u':
 | 
						|
				time_fmt = TIME_ACCESS;
 | 
						|
				break;
 | 
						|
			case 'c':
 | 
						|
				time_fmt = TIME_CHANGE;
 | 
						|
				break;
 | 
						|
			case 'e':
 | 
						|
				opts |= DISP_FULLTIME;
 | 
						|
				break;
 | 
						|
#endif
 | 
						|
#ifdef BB_FEATURE_LS_RECURSIVE
 | 
						|
			case 'R':
 | 
						|
				opts |= DISP_RECURSIVE;
 | 
						|
				break;
 | 
						|
#endif
 | 
						|
			case 'g': /* ignore -- for ftp servers */
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				goto print_usage_message;
 | 
						|
			}
 | 
						|
 | 
						|
		argi++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* choose a display format */
 | 
						|
	if (display_fmt == FMT_AUTO)
 | 
						|
		display_fmt = isatty(fileno(stdout)) ? FMT_COLUMNS : FMT_SINGLE;
 | 
						|
	if (argi < argc - 1)
 | 
						|
		opts |= DISP_DIRNAME;	/* 2 or more items? label directories */
 | 
						|
#ifdef BB_FEATURE_AUTOWIDTH
 | 
						|
	/* could add a -w option and/or TIOCGWINSZ call */
 | 
						|
	if (terminal_width < 1)
 | 
						|
		terminal_width = TERMINAL_WIDTH;
 | 
						|
 | 
						|
	for (i = argi; i < argc; i++) {
 | 
						|
		int len = strlen(argv[i]);
 | 
						|
 | 
						|
		if (toplevel_column_width < len)
 | 
						|
			toplevel_column_width = len;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* process files specified, or current directory if none */
 | 
						|
#ifdef BB_FEATURE_LS_RECURSIVE
 | 
						|
	if (opts & DISP_RECURSIVE) {
 | 
						|
		i = 0;
 | 
						|
		if (argi == argc) {
 | 
						|
			i = recursiveAction(".", TRUE, FALSE, FALSE, NULL, dirAction, NULL);
 | 
						|
		}
 | 
						|
		while (argi < argc) {
 | 
						|
			i |= recursiveAction(argv[argi++], TRUE, FALSE, FALSE, NULL, dirAction, NULL);
 | 
						|
		}
 | 
						|
	} else 
 | 
						|
#endif
 | 
						|
	{
 | 
						|
		i = 0;
 | 
						|
		if (argi == argc)
 | 
						|
			i = list_item(".");
 | 
						|
		while (argi < argc)
 | 
						|
			i |= list_item(argv[argi++]);
 | 
						|
		newline();
 | 
						|
	}
 | 
						|
	exit(i);
 | 
						|
 | 
						|
  print_usage_message:
 | 
						|
	usage(ls_usage);
 | 
						|
	exit(FALSE);
 | 
						|
}
 |