716 lines
15 KiB
C
716 lines
15 KiB
C
/* $Id$ */
|
|
/* Copyright (c) 2004-2015 Pierre Pronchery <khorben@defora.org> */
|
|
/* This file is part of DeforaOS Unix utils */
|
|
/* 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, version 3 of the License.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>. */
|
|
/* TODO: use a dynamic array and qsort() instead of lists */
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
|
|
/* constants */
|
|
#define PROGNAME "ls"
|
|
|
|
/* macros */
|
|
#define max(a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
|
|
/* Prefs */
|
|
typedef int Prefs;
|
|
#define LS_PREFS_C 00001
|
|
#define LS_PREFS_F 00002
|
|
#define LS_PREFS_R 00004
|
|
#define LS_PREFS_a 00010
|
|
#define LS_PREFS_c 00020
|
|
#define LS_PREFS_d 00040
|
|
#define LS_PREFS_l 00100
|
|
#define LS_PREFS_t 00200
|
|
#define LS_PREFS_u 00400
|
|
#define LS_PREFS_1 01000
|
|
#define LS_PREFS_H 02000
|
|
#define LS_PREFS_L 04000
|
|
|
|
static int _prefs_parse(Prefs * prefs, int argc, char * argv[])
|
|
{
|
|
int o;
|
|
|
|
memset(prefs, 0, sizeof(Prefs));
|
|
while((o = getopt(argc, argv, "CFRacdltu1HL")) != -1)
|
|
switch(o)
|
|
{
|
|
case 'C':
|
|
*prefs -= *prefs & LS_PREFS_1;
|
|
*prefs |= LS_PREFS_C;
|
|
break;
|
|
case 'F':
|
|
*prefs |= LS_PREFS_F;
|
|
break;
|
|
case 'R':
|
|
*prefs |= LS_PREFS_R;
|
|
break;
|
|
case 'a':
|
|
*prefs |= LS_PREFS_a;
|
|
break;
|
|
case 'c':
|
|
*prefs -= *prefs & LS_PREFS_u;
|
|
*prefs |= LS_PREFS_c;
|
|
break;
|
|
case 'd':
|
|
*prefs |= LS_PREFS_d;
|
|
break;
|
|
case 'l':
|
|
*prefs |= LS_PREFS_l;
|
|
break;
|
|
case 't':
|
|
*prefs |= LS_PREFS_t;
|
|
break;
|
|
case 'u':
|
|
*prefs -= *prefs & LS_PREFS_c;
|
|
*prefs |= LS_PREFS_u;
|
|
break;
|
|
case '1':
|
|
*prefs |= LS_PREFS_1;
|
|
break;
|
|
case 'H':
|
|
*prefs -= *prefs & LS_PREFS_L;
|
|
*prefs |= LS_PREFS_H;
|
|
break;
|
|
case 'L':
|
|
*prefs -= *prefs & LS_PREFS_H;
|
|
*prefs |= LS_PREFS_L;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* SList */
|
|
/* types */
|
|
/* SListCell */
|
|
typedef struct _SListCell
|
|
{
|
|
void * data;
|
|
struct _SListCell * next;
|
|
} SListCell;
|
|
typedef SListCell * SList;
|
|
|
|
/* functions */
|
|
static SListCell * _slistcell_new(void * data, SListCell * next)
|
|
{
|
|
SListCell * slc;
|
|
|
|
if((slc = malloc(sizeof(SListCell))) == NULL)
|
|
return NULL;
|
|
slc->data = data;
|
|
slc->next = next;
|
|
return slc;
|
|
}
|
|
|
|
static void _slistcell_delete(SListCell * slistcell)
|
|
{
|
|
free(slistcell);
|
|
}
|
|
|
|
static SList * slist_new(void)
|
|
{
|
|
SList * sl;
|
|
|
|
if((sl = malloc(sizeof(SList))) == NULL)
|
|
return NULL;
|
|
*sl = NULL;
|
|
return sl;
|
|
}
|
|
|
|
static void slist_delete(SList * slist)
|
|
{
|
|
SListCell * slc = *slist;
|
|
SListCell * p;
|
|
|
|
while(slc != NULL)
|
|
{
|
|
p = slc->next;
|
|
_slistcell_delete(slc);
|
|
slc = p;
|
|
}
|
|
free(slist);
|
|
}
|
|
|
|
/* returns */
|
|
static void * slist_data(SList * slist)
|
|
{
|
|
if(*slist == NULL)
|
|
return NULL;
|
|
return (*slist)->data;
|
|
}
|
|
|
|
static void slist_next(SList * slist)
|
|
{
|
|
if(*slist == NULL)
|
|
return;
|
|
*slist = (*slist)->next;
|
|
}
|
|
|
|
static void slist_apply(SList * slist, int (*func)(void *))
|
|
{
|
|
SListCell * slc = *slist;
|
|
|
|
for(slc = *slist; slc != NULL; slc = slc->next)
|
|
func(slc->data);
|
|
}
|
|
|
|
static int slist_insert_sorted(SList * slist, void * data,
|
|
int (*func)(void *, void *))
|
|
{
|
|
SListCell * slc = *slist;
|
|
SListCell * p = NULL;
|
|
|
|
while(slc != NULL && func(slc->data, data) < 0)
|
|
{
|
|
p = slc;
|
|
slc = slc->next;
|
|
}
|
|
if(slc == NULL) /* empty or last */
|
|
{
|
|
if((slc = _slistcell_new(data, NULL)) == NULL)
|
|
return 1;
|
|
if(p == NULL)
|
|
*slist = slc; /* empty */
|
|
else
|
|
p->next = slc; /* last */
|
|
return 0;
|
|
}
|
|
if((p = _slistcell_new(slc->data, slc->next)) == NULL)
|
|
return 1;
|
|
slc->data = data;
|
|
slc->next = p;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* ls */
|
|
static int _ls_error(char const * message, int ret);
|
|
typedef int (*compare_func)(void *, void *);
|
|
static compare_func _ls_compare(Prefs * prefs);
|
|
static int _ls_directory_do(Prefs * prefs, char const * directory);
|
|
static int _ls_args(SList ** files, SList ** dirs);
|
|
static int _is_directory(Prefs * prefs, char const * filename);
|
|
static int _ls_do(Prefs * prefs, int argc, char const * directory,
|
|
SList * files, SList * dirs);
|
|
static int _ls(int argc, char * argv[], Prefs * prefs)
|
|
{
|
|
SList * files;
|
|
SList * dirs;
|
|
compare_func cmp = _ls_compare(prefs);
|
|
int ret = 0;
|
|
int i;
|
|
int isdir;
|
|
char * str;
|
|
|
|
if(argc == 0 && !(*prefs & LS_PREFS_d))
|
|
return _ls_directory_do(prefs, ".");
|
|
if(_ls_args(&files, &dirs) != 0)
|
|
return 2;
|
|
if(argc == 0)
|
|
ret |= slist_insert_sorted(files, strdup("."), cmp);
|
|
for(i = 0; i < argc; i++)
|
|
{
|
|
if((isdir = _is_directory(prefs, argv[i])) == 2)
|
|
ret |= 1;
|
|
else if((str = strdup(argv[i])) == NULL)
|
|
ret |= _ls_error("malloc", 1);
|
|
else if(*prefs & LS_PREFS_d)
|
|
ret |= slist_insert_sorted(files, str, cmp);
|
|
else
|
|
ret |= slist_insert_sorted(isdir ? dirs : files, str,
|
|
cmp);
|
|
}
|
|
ret |= _ls_do(prefs, argc, NULL, files, dirs);
|
|
return ret == 0 ? 0 : 1;
|
|
}
|
|
|
|
static int _ls_error(char const * message, int ret)
|
|
{
|
|
fputs(PROGNAME ": ", stderr);
|
|
perror(message);
|
|
return ret;
|
|
}
|
|
|
|
static int _acccmp(char * a, char * b);
|
|
static int _modcmp(char * a, char * b);
|
|
static compare_func _ls_compare(Prefs * prefs)
|
|
{
|
|
if(!(*prefs & LS_PREFS_t))
|
|
return (compare_func)strcmp;
|
|
if(*prefs & LS_PREFS_u)
|
|
return (compare_func)_acccmp;
|
|
return (compare_func)_modcmp;
|
|
}
|
|
|
|
static int _acccmp(char * a, char * b)
|
|
{
|
|
struct stat sta;
|
|
struct stat stb;
|
|
|
|
if(lstat(a, &sta) != 0)
|
|
return _ls_error(a, 0);
|
|
if(lstat(b, &stb) != 0)
|
|
return _ls_error(b, 0);
|
|
return sta.st_atime - stb.st_atime;
|
|
}
|
|
|
|
static int _modcmp(char * a, char * b)
|
|
{
|
|
struct stat sta;
|
|
struct stat stb;
|
|
|
|
if(lstat(a, &sta) != 0)
|
|
return _ls_error(a, 0);
|
|
if(lstat(b, &stb) != 0)
|
|
return _ls_error(b, 0);
|
|
return sta.st_mtime - stb.st_mtime;
|
|
}
|
|
|
|
static int _ls_directory_do(Prefs * prefs, char const * directory)
|
|
{
|
|
SList * files;
|
|
SList * dirs;
|
|
compare_func cmp = _ls_compare(prefs);
|
|
DIR * dir;
|
|
struct dirent * de;
|
|
char * file = NULL;
|
|
char * p;
|
|
int pos = 1;
|
|
|
|
if((dir = opendir(directory)) == NULL)
|
|
return _ls_error(directory, 1);
|
|
_ls_args(&files, &dirs);
|
|
for(; (de = readdir(dir)) != NULL; pos++)
|
|
{
|
|
if(*(de->d_name) == '.' && !(*prefs & LS_PREFS_a))
|
|
continue;
|
|
slist_insert_sorted(files, strdup(de->d_name), cmp);
|
|
if(pos <= 2)
|
|
continue;
|
|
if((p = realloc(file, strlen(directory) + strlen(de->d_name)
|
|
+ 2)) == NULL)
|
|
{
|
|
_ls_error("malloc", 0);
|
|
continue;
|
|
}
|
|
file = p;
|
|
sprintf(file, "%s/%s", directory, de->d_name);
|
|
if((*prefs & LS_PREFS_R) && _is_directory(prefs, file) == 1)
|
|
slist_insert_sorted(dirs, strdup(file), cmp);
|
|
}
|
|
free(file);
|
|
if(closedir(dir) != 0)
|
|
_ls_error(directory, 0);
|
|
_ls_do(prefs, 2, directory, files, dirs);
|
|
return 0;
|
|
}
|
|
|
|
static int _ls_args(SList ** files, SList ** dirs)
|
|
{
|
|
if((*files = slist_new()) == NULL)
|
|
return _ls_error("slist", 1);
|
|
if((*dirs = slist_new()) == NULL)
|
|
{
|
|
slist_delete(*files);
|
|
return _ls_error("slist", 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int _is_directory(Prefs * prefs, char const * filename)
|
|
{
|
|
int (*_stat)(const char * filename, struct stat * buf) = lstat;
|
|
struct stat st;
|
|
|
|
if(*prefs & LS_PREFS_H || *prefs & LS_PREFS_L)
|
|
_stat = stat;
|
|
if((_stat(filename, &st)) != 0)
|
|
return _ls_error(filename, 2);
|
|
return S_ISDIR(st.st_mode) ? 1 : 0;
|
|
}
|
|
|
|
static int _ls_do_files(Prefs * prefs, char const * directory, SList * files);
|
|
static int _ls_do_dirs(Prefs * prefs, int argc, SList * dirs);
|
|
static int _ls_do(Prefs * prefs, int argc, char const * directory,
|
|
SList * files, SList * dirs)
|
|
{
|
|
int res = 0;
|
|
char sep = 0;
|
|
|
|
if(slist_data(files) != NULL && slist_data(dirs) != NULL)
|
|
sep = '\n';
|
|
res += _ls_do_files(prefs, directory, files);
|
|
if(sep != 0)
|
|
fputc(sep, stdout);
|
|
res += _ls_do_dirs(prefs, argc, dirs);
|
|
return res;
|
|
}
|
|
|
|
static int _ls_free(void * data);
|
|
static int _ls_do_files_short(Prefs * prefs, char const * directory,
|
|
SList * files);
|
|
static int _ls_do_files_long(Prefs * prefs, char const * directory,
|
|
SList * files);
|
|
static int _ls_do_files(Prefs * prefs, char const * directory, SList * files)
|
|
{
|
|
int res = 0;
|
|
|
|
if(*prefs & LS_PREFS_l)
|
|
res = _ls_do_files_long(prefs, directory, files);
|
|
else
|
|
res = _ls_do_files_short(prefs, directory, files);
|
|
slist_apply(files, _ls_free);
|
|
slist_delete(files);
|
|
return res;
|
|
}
|
|
|
|
static char _short_file_mode(Prefs * prefs, char const * directory,
|
|
char const * file);
|
|
static int _ls_do_files_short(Prefs * prefs, char const * directory,
|
|
SList * files)
|
|
{
|
|
char * cols;
|
|
char * p;
|
|
size_t len = 0;
|
|
size_t lencur;
|
|
size_t lenmax = 0;
|
|
size_t colnb = 0;
|
|
size_t i = 0;
|
|
size_t j = 0;
|
|
char c;
|
|
SList cur;
|
|
|
|
if(((*prefs & LS_PREFS_1) == 0) && (cols = getenv("COLUMNS")) != NULL
|
|
&& *cols != '\0' && (len = strtol(cols, &p, 10)) > 0
|
|
&& *p == '\0')
|
|
{
|
|
for(cur = *files; cur != NULL; slist_next(&cur))
|
|
{
|
|
lencur = strlen(slist_data(&cur));
|
|
lenmax = max(lenmax, lencur);
|
|
}
|
|
if(*prefs & LS_PREFS_F)
|
|
lenmax++;
|
|
if(lenmax > 0)
|
|
colnb = len / ++lenmax;
|
|
}
|
|
for(cur = *files; cur != NULL; slist_next(&cur))
|
|
{
|
|
p = slist_data(&cur);
|
|
j = strlen(p);
|
|
fwrite(p, sizeof(char), j, stdout);
|
|
if((*prefs & LS_PREFS_F)
|
|
&& (c = _short_file_mode(prefs, directory, p)))
|
|
{
|
|
fputc(c, stdout);
|
|
j++;
|
|
}
|
|
if(i + 1 < colnb)
|
|
{
|
|
for(i++; j < lenmax; j++)
|
|
fputc(' ', stdout);
|
|
continue;
|
|
}
|
|
fputc('\n', stdout);
|
|
i = 0;
|
|
}
|
|
if(i != 0)
|
|
fputc('\n', stdout);
|
|
return 0;
|
|
}
|
|
|
|
static char _file_mode_letter(mode_t mode);
|
|
static char _short_file_mode(Prefs * prefs, char const * directory,
|
|
char const * file)
|
|
{
|
|
int (* _stat)(const char * filename, struct stat * buf) = lstat;
|
|
struct stat st;
|
|
char * p;
|
|
|
|
if(*prefs & LS_PREFS_H || *prefs & LS_PREFS_L)
|
|
_stat = stat;
|
|
if(directory == NULL)
|
|
{
|
|
if(_stat(file, &st) != 0)
|
|
return _ls_error(file, 0);
|
|
}
|
|
else
|
|
{
|
|
if((p = malloc(strlen(directory) + 1 + strlen(file) + 1))
|
|
== NULL)
|
|
return _ls_error("malloc", 0);
|
|
sprintf(p, "%s/%s", directory, file);
|
|
if(_stat(p, &st) != 0)
|
|
{
|
|
free(p);
|
|
return _ls_error(file, 0);
|
|
}
|
|
free(p);
|
|
}
|
|
return _file_mode_letter(st.st_mode);
|
|
}
|
|
|
|
static void _long_print(Prefs * prefs, char const * filename,
|
|
char const * basename, struct stat * st);
|
|
static int _ls_do_files_long(Prefs * prefs, char const * directory,
|
|
SList * files)
|
|
{
|
|
SList cur;
|
|
char * file = NULL;
|
|
char * p;
|
|
int (* _stat)(const char * filename, struct stat * buf) = lstat;
|
|
struct stat st;
|
|
|
|
if(*prefs & LS_PREFS_H || *prefs & LS_PREFS_L)
|
|
_stat = stat;
|
|
for(cur = *files; cur != NULL; slist_next(&cur))
|
|
{
|
|
p = slist_data(&cur);
|
|
if(directory != NULL)
|
|
{
|
|
if((p = realloc(file, strlen(directory) + strlen(p)
|
|
+ 2)) == NULL)
|
|
{
|
|
_ls_error("malloc", 0);
|
|
continue;
|
|
}
|
|
file = p;
|
|
p = slist_data(&cur);
|
|
sprintf(file, "%s/%s", directory, p);
|
|
}
|
|
if(_stat(directory == NULL ? p : file, &st) != 0)
|
|
_ls_error(file, 0);
|
|
else
|
|
_long_print(prefs, file != NULL ? file : p, p, &st);
|
|
}
|
|
free(file);
|
|
return 0;
|
|
}
|
|
|
|
/* _long_print */
|
|
static void _long_mode(char str[11], mode_t mode);
|
|
static char const * _long_owner(uid_t uid);
|
|
static char const * _long_group(gid_t gid);
|
|
static char const * _long_date(time_t date);
|
|
static char _file_mode_letter(mode_t mode);
|
|
static void _print_link(char const * filename);
|
|
|
|
static void _long_print(Prefs * prefs, char const * filename,
|
|
char const * basename, struct stat * st)
|
|
{
|
|
char mode[11];
|
|
char const * owner;
|
|
char const * group;
|
|
char const * date;
|
|
|
|
_long_mode(mode, st->st_mode);
|
|
owner = _long_owner(st->st_uid);
|
|
group = _long_group(st->st_gid);
|
|
if(*prefs & LS_PREFS_u)
|
|
date = _long_date(st->st_atime);
|
|
else if(*prefs & LS_PREFS_c)
|
|
date = _long_date(st->st_ctime);
|
|
else
|
|
date = _long_date(st->st_mtime);
|
|
printf("%s %2u %-7s %-7s %6u %s %s", mode, (unsigned)st->st_nlink,
|
|
owner, group, (unsigned)st->st_size, date, basename);
|
|
if(S_ISLNK(st->st_mode) && !(*prefs & LS_PREFS_L))
|
|
/* FIXME not in POSIX? */
|
|
_print_link(filename);
|
|
else if(*prefs & LS_PREFS_F)
|
|
fputc(_file_mode_letter(st->st_mode), stdout);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
static void _long_mode(char str[11], mode_t mode)
|
|
{
|
|
unsigned int i;
|
|
|
|
for(i = 0; i < 10; i++)
|
|
str[i] = '-';
|
|
if(!S_ISREG(mode))
|
|
{
|
|
if(S_ISLNK(mode))
|
|
str[0] = 'l';
|
|
else if(S_ISBLK(mode))
|
|
str[0] = 'b';
|
|
else if(S_ISCHR(mode))
|
|
str[0] = 'c';
|
|
else if(S_ISFIFO(mode))
|
|
str[0] = 'p';
|
|
else if(S_ISDIR(mode))
|
|
str[0] = 'd';
|
|
}
|
|
if(mode & S_IRUSR)
|
|
str[1] = 'r';
|
|
if(mode & S_IWUSR)
|
|
str[2] = 'w';
|
|
if(mode & S_IXUSR)
|
|
str[3] = (mode & S_ISUID ? 's' : 'x');
|
|
else if(mode & S_ISUID)
|
|
str[3] = 'S';
|
|
if(mode & S_IRGRP)
|
|
str[4] = 'r';
|
|
if(mode & S_IWGRP)
|
|
str[5] = 'w';
|
|
if(mode & S_IXGRP)
|
|
str[6] = 'x';
|
|
if(mode & S_IROTH)
|
|
str[7] = 'r';
|
|
if(mode & S_IWOTH)
|
|
str[8] = 'w';
|
|
if(mode & S_IXOTH)
|
|
str[9] = 'x';
|
|
str[10] = '\0';
|
|
}
|
|
|
|
static char const * _long_owner(uid_t uid)
|
|
{
|
|
static char buf[12];
|
|
struct passwd * pw;
|
|
|
|
if((pw = getpwuid(uid)) != NULL)
|
|
return pw->pw_name;
|
|
snprintf(buf, sizeof(buf), "%u", uid);
|
|
return buf;
|
|
}
|
|
|
|
static char const * _long_group(gid_t gid)
|
|
{
|
|
static char buf[12];
|
|
struct group * grp;
|
|
|
|
if((grp = getgrgid(gid)) != NULL)
|
|
return grp->gr_name;
|
|
snprintf(buf, sizeof(buf), "%u", gid);
|
|
return buf;
|
|
}
|
|
|
|
static char const * _long_date(time_t date)
|
|
{
|
|
static char buf[15];
|
|
struct tm tm;
|
|
static time_t sixmonths = -1;
|
|
size_t len;
|
|
|
|
if(sixmonths == -1)
|
|
sixmonths = time(NULL) - 15552000;
|
|
localtime_r(&date, &tm);
|
|
if(date < sixmonths)
|
|
len = strftime(buf, sizeof(buf), "%b %e %Y", &tm);
|
|
else
|
|
len = strftime(buf, sizeof(buf), "%b %e %H:%M", &tm);
|
|
buf[len] = '\0';
|
|
return buf;
|
|
}
|
|
|
|
static char _file_mode_letter(mode_t mode)
|
|
{
|
|
if(S_ISLNK(mode))
|
|
return '@';
|
|
if(S_ISDIR(mode))
|
|
return '/';
|
|
if(S_ISFIFO(mode))
|
|
return '|';
|
|
if(mode & (S_IXUSR | S_IXGRP | S_IXOTH))
|
|
return '*';
|
|
return ' ';
|
|
}
|
|
|
|
static void _print_link(char const * filename)
|
|
{
|
|
char buf[PATH_MAX+1];
|
|
int len;
|
|
|
|
if((len = readlink(filename, buf, sizeof(buf)-1)) == -1)
|
|
{
|
|
_ls_error(filename, 0);
|
|
return;
|
|
}
|
|
buf[len] = '\0';
|
|
printf("%s%s", " -> ", buf);
|
|
}
|
|
|
|
static int _ls_free(void * data)
|
|
{
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
static int _ls_do_dirs(Prefs * prefs, int argc, SList * dirs)
|
|
{
|
|
int res = 0;
|
|
SList cur;
|
|
char * dir = NULL;
|
|
char * eol = "";
|
|
|
|
for(cur = *dirs; cur != NULL; slist_next(&cur))
|
|
{
|
|
dir = slist_data(&cur);
|
|
if(argc != 1)
|
|
printf("%s%s%s", eol, dir, ":\n");
|
|
eol = "\n";
|
|
res += _ls_directory_do(prefs, dir);
|
|
}
|
|
slist_apply(dirs, _ls_free);
|
|
slist_delete(dirs);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* usage */
|
|
static int _usage(void)
|
|
{
|
|
fputs("Usage: " PROGNAME " [-CFRacdilqrtu1][-H | -L]\n\
|
|
-C Write multi-column output\n\
|
|
-F Write a symbol after files names depending on their type\n\
|
|
-R Recursively list subdirectories encountered\n\
|
|
-a Write out all hidden directory entries\n\
|
|
-c Use time of last modification of file status\n\
|
|
-d Treat directories like files\n", stderr);
|
|
fputs(" -l Write out in long format\n\
|
|
-t Sort with the last modified file first\n\
|
|
-u Use time of last access\n\
|
|
-1 Force output to be one entry per line\n\
|
|
-H Dereference symbolic links\n\
|
|
-L Evaluate symbolic links\n", stderr);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* main */
|
|
int main(int argc, char * argv[])
|
|
{
|
|
Prefs p;
|
|
|
|
if(_prefs_parse(&p, argc, argv) != 0)
|
|
return _usage();
|
|
return _ls(argc - optind, &argv[optind], &p) == 0 ? 0 : 1;
|
|
}
|