/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1989-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* ls -- list file status
*/
static const char usage[] =
"[-?\n@(#)$Id: ls (AT&T Research) 2012-04-20 $\n]"
"[+DESCRIPTION?For each directory argument \bls\b lists the contents; for each"
" file argument the name and requested information are listed."
" The directory \b.\b is assumed if no file arguments appear."
" The listing is sorted by file name by default, except that file"
" arguments are listed before directories.]"
"[+?Multi-column terminal output display width is determined by \bioctl\b(2)"
"[+?\bgetconf PATH_RESOLVE\b determines how symbolic links are handled. This"
" can be explicitly overridden by the \b--logical\b, \b--metaphysical\b,"
" and \b--physical\b options below. \bPATH_RESOLVE\b can be one of:]{"
" [+logical?Follow all symbolic links.]"
" [+metaphysical?Follow command argument symbolic links,"
" otherwise don't follow.]"
" [+physical?Don't follow symbolic links.]"
"}"
"[a:all?List entries starting with \b.\b; turns off \b--almost-all\b.]"
"[A:almost-all?List all entries but \b.\b and \b..\b; turns off \b--all\b.]"
"[b:escape?Print escapes for nongraphic characters.]"
"[B:ignore-backups?Do not list entries ending with ~.]"
"[c:ctime?Sort by change time; list ctime with \b--long\b.]"
"[C:multi-column?List entries by columns.]"
"[d:directory?List directory entries instead of contents.]"
"[D:define?Define \akey\a with optional \avalue\a. \avalue\a will be expanded"
" when \b%(\b\akey\a\b)\b is specified in \b--format\b. \akey\a may"
" override internal \b--format\b identifiers.]:[key[=value]]]"
"[e:long-iso|long-time?Equivalent to \b--long --time-style=long-iso\b.]"
"[E:full-iso|full-time?Equivalent to \b--long --time-style=full-iso\b.]"
"[f:force?Force each argument to be interpreted as a directory and list"
" the name found in each slot in the physical directory order. Turns"
" on \b-aU\b and turns off \b-lrst\b. The results are undefined for"
" non-directory arguments.]"
"[Z:format?Append to the listing format string. \aformat\a follows"
" \bprintf\b(3) conventions, except that \bsfio\b(3) inline ids"
" are used instead of arguments:"
" %[-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a[:\asubformat\a]])\achar\a."
" If \achar\a is \bs\b then the string form of the item is listed,"
" otherwise the corresponding numeric form is listed. \asubformat\a"
" overrides the default formatting for \aid\a. Supported \aid\as"
" and \asubformat\as are:]:[format]{"
" [+atime?access time]"
" [+blocks?size in blocks]"
" [+ctime?change time]"
" [+devmajor?major device number]"
" [+devminor?minor device number]"
" [+dir.blocks?directory blocks]"
" [+dir.bytes?directory size in bytes]"
" [+dir.count?directory entry count]"
" [+dir.files?directory file count]"
" [+flags?command line flags in effect]"
" [+gid?group id]"
" [+header?listing header]"
" [+ino?serial number]"
" [+linkop?link operation: -> for symbolic, empty otherwise]"
" [+linkname?symbolic link text]"
" [+linkpath?symbolic link text]"
" [+mark?file or directory mark character]"
" [+markdir?directory mark character]"
" [+mode?access mode]"
" [+mtime?modification time]"
" [+name?entry name]"
" [+nlink?hard link count]"
" [+path?file path from original root dir]"
" [+perm?access permissions]"
" [+size?file size in bytes]"
" [+summary?listing summary info]"
" [+total.blocks?running total block count]"
" [+total.bytes?running total size in bytes]"
" [+total.files?running total file count]"
" [+trailer?listing trailer]"
" [+uid?owner id]"
" [+view?3d fs view level, 0 for the top or 2d]"
" [+----?subformats ----]"
" [+case\b::\bp\b\a1\a::\bs\b\a1\a::...::\bp\b\an\a::\bs\b\an\a?Expands"
" to \bs\b\ai\a if the value of \aid\a matches the shell"
" pattern \bp\b\ai\a, or the empty string if there is no"
" match.]"
" [+mode?The integral value as a \bfmtmode\b(3) string.]"
" [+perm?The integral value as a \bfmtperm\b(3) string.]"
" [+time[=\aformat\a]]?The integral value as a \bstrftime\b(3)"
" string. For example,"
" \b--format=\"%8(mtime)u %(ctime:time=%H:%M:%S)s\"\b"
" lists the mtime in seconds since the epoch and the"
" ctime as hours:minutes:seconds.]"
" }"
"[F:classify?Append a character for typing each entry. Turns on \b--physical\b.]"
"[g:group?\b--long\b with no owner info.]"
"[G?\b--long\b with no group info.]"
"[h:scale|binary-scale|human-readable?Scale sizes to powers of 1024 { Ki Mi Gi Ti Pi Xi }.]"
"[i:inode?List the file serial number.]"
"[I:ignore?Do not list implied entries matching shell \apattern\a.]:[pattern]"
"[k:kilobytes?Use 1024 blocks instead of 512.]"
"[K:shell-quote?Enclose entry names in shell $'...' if necessary.]"
"[l:long|verbose?Use a long listing format.]"
"[m:commas|comma-list?List names as comma separated list.]"
"[n:numeric-uid-gid?List numeric user and group ids instead of names.]"
"[N:literal|show-controls-chars?Print raw entry names (don't treat e.g. control characters specially).]"
"[o:owner?\b--long\b with no group info.]"
"[O?\b--long\b with no owner info.]"
"[p:markdir?Append / to each directory name.]"
"[q:hide-control-chars?Print ? instead of non graphic characters.]"
"[Q:quote-name?Enclose all entry names in \"...\".]"
"[J:quote-style|quoting-style?Quote entry names according to \astyle\a:]:[style:=question]{"
" [c:C?C \"...\" quote.]"
" [e:escape?\b\\\b escape if necessary.]"
" [l:literal?No quoting.]"
" [q:question?Replace unprintable characters with \b?\b.]"
" [s:shell?Shell $'...' quote if necessary.]"
" [S:shell-always?Shell $'...' every name.]"
"}"
"[r:reverse?Reverse order while sorting.]"
"[R:recursive?List subdirectories recursively.]"
"[s:size?Print size of each file, in blocks.]"
"[S:bysize?Sort by file size.]"
"[t:?Sort by modification time; list mtime with \b--long\b.]"
"[T:tabsize?Ignored by this implementation.]#[columns]"
"[u:access?Sort by last access time; list atime with \b--long\b.]"
"[U?Equivalent to \b--sort=none\b.]"
"[V:colors|colours?\akey\a determines when color is used to distinguish"
" types:]:?[key:=never]{"
" [n:never?Never use color.]"
" [a:always?Always use color.]"
" [t:tty|auto?Use color when output is a tty.]"
"}"
"[w:width?Set the screen width to \ascreen-width\a and the screen height"
" to \ascreen-height\a if specified.]:[[screen-heightX]]screen-width]"
"[W:time?Display \akey\a time instead of the modification time:]:[key]{"
" [a:atime|access|use?access time]"
" [c:ctime|status?status change time]"
" [m:mtime|time?modify time]"
"}"
"[x:across?List entries by lines instead of by columns.]"
"[X:extension?Sort alphabetically by entry extension.]"
"[y:sort?Sort by \akey\a:]:?[key]{"
" [a:atime|access|use?Access time.]"
" [c:ctime|status?Status change time.]"
" [x:extension?File name extension.]"
" [m:mtime|time?Modify time.]"
" [f:name?File name.]"
" [n:none?Don't sort.]"
" [s:size|blocks?File size.]"
" [v:version?File name version.]"
"}"
"[Y:layout?Listing layout \akey\a:]:[key]{"
" [a:across|horizontal?Multi-column across the page.]"
" [c:comma?Comma separated names across the page.]"
" [l:long|verbose?Long listing.]"
" [v:multi-column|vertical?Multi-column by column.]"
" [1:single-column?One column down the page.]"
"}"
"[z:time-style?List the time according to \astyle\a:]:[style]{"
" [10:posix-iso?No change for the C or posix locales, \biso\b otherwise.]"
" [11:posix-full-iso?No change for the C or posix locales, \bfull-iso\b"
" otherwise.]"
" [12:+\aformat\a?A \bdate\b(1) +\aformat\a.]"
"}"
"[1:one-column?List one file per line.]"
"[L:logical|follow?Follow symbolic links. The default is determined by"
" \bgetconf PATH_RESOLVE\b.]"
"[H:metaphysical?Follow command argument symbolic links, otherwise don't"
" follow. The default is determined by \bgetconf PATH_RESOLVE\b.]"
"[P:physical?Don't follow symbolic links. The default is determined by"
" \bgetconf PATH_RESOLVE\b.]"
"[101:block-size?Use \ablocksize\a blocks.]#[blocksize]"
"[102:decimal-scale|thousands?Scale sizes to powers of 1000 { K M G T P X }.]"
"[103:dump?Print the generated \b--format\b string on the standard output"
" and exit.]"
"[104:testdate?\b--format\b time values newer than \adate\a will be printed"
" as \adate\a. Used for regression testing.]:[date]"
"[105:testsize?Shift file sizes left \ashift\a bits and set file block counts"
" to the file size divided by 512. Used for regression testing.]#[shift]"
"\n"
"\n[ file ... ]\n"
"\n"
"[+SEE ALSO?\bchmod\b(1), \bfind\b(1), \bgetconf\b(1), \btw\b(1)]"
"[+BUGS?Can we add options to something else now?]"
;
#include <ast.h>
#include <ls.h>
#include <ctype.h>
#include <error.h>
#include <ftwalk.h>
#include <sfdisc.h>
#include <hash.h>
#include <tmx.h>
#include <fs3d.h>
#define VISIBLE(f) ((f)->level<=0||(!state.ignore||!strmatch((f)->name,state.ignore))&&(!(state.lsflags&LS_NOBACKUP)||(f)->name[(f)->namelen-1]!='~')&&((state.lsflags&LS_ALL)||(f)->name[0]!='.'||(state.lsflags&LS_MOST)&&((f)->name[1]&&(f)->name[1]!='.'||(f)->name[2])))
#if 0
#define BLOCKS(st) ((state.blocksize==LS_BLOCKSIZE)?iblocks(st):(state.blocksize>LS_BLOCKSIZE)?(iblocks(st)+state.blocksize/LS_BLOCKSIZE-1)/(state.blocksize/LS_BLOCKSIZE):iblocks(st)*(LS_BLOCKSIZE/state.blocksize))
#else
#define BLOCKS(st) ((state.blocksize==LS_BLOCKSIZE)?iblocks(st):(iblocks(st)*LS_BLOCKSIZE+state.blocksize-1)/state.blocksize)
#endif
{
} Count_t;
typedef struct /* sfkeyprintf() keys */
{
} Key_t;
typedef struct /* list state */
{
} List_t;
typedef struct /* program state */
{
} State_t;
static char DEF_header[] =
"%(dir.count:case;0;;1;%(path)s:\n;*;\n%(path)s:\n)s"
"%(flags:case;*d*;;*[ls]*;total %(dir.blocks)u\n)s"
;
{
{ 0 },
{ "atime", KEY_atime },
{ "blocks", KEY_blocks },
{ "ctime", KEY_ctime },
{ "dev", KEY_dev },
{ "device", KEY_device },
{ "devmajor", KEY_devmajor },
{ "devminor", KEY_devminor },
{ "dir.blocks", KEY_dir_blocks },
{ "dir.bytes", KEY_dir_bytes },
{ "dir.count", KEY_dir_count },
{ "dir.files", KEY_dir_files },
{ "flags", KEY_flags },
{ "gid", KEY_gid },
{ "ino", KEY_ino },
{ "linkop", KEY_linkop },
{ "linkpath", KEY_linkpath },
{ "mark", KEY_mark },
{ "markdir", KEY_markdir },
{ "mode", KEY_mode },
{ "mtime", KEY_mtime },
{ "name", KEY_name },
{ "nlink", KEY_nlink },
{ "path", KEY_path },
{ "perm", KEY_perm },
{ "size", KEY_size },
{ "summary", KEY_summary },
{ "total.blocks", KEY_total_blocks },
{ "total.bytes", KEY_total_bytes },
{ "total.files", KEY_total_files },
{ "trailer", KEY_trailer },
{ "uid", KEY_uid },
{ "view", KEY_view },
/* aliases */
{ "linkname", KEY_linkpath },
};
/*
* return a copy of s with unprintable chars replaced by ?
*/
static char*
printable(register char* s)
{
register char* t;
register char* p;
register int c;
static char* prdata;
static int prsize;
{
return fmtesc(s);
}
c = strlen(s) + 4;
if (c > prsize)
{
}
t = prdata;
*t++ = '"';
if (!mbwide())
while (c = *s++)
else
for (p = s; c = mbchar(s);)
if (c < 0)
{
s++;
*t++ = '?';
}
else if (mbwidth(c) <= 0)
*t++ = '?';
else
while (p < s)
*t++ = *p++;
*t++ = '"';
*t = 0;
return prdata;
}
/*
* sfkeyprintf() lookup
*/
static int
{
register char* s = 0;
register Sflong_t n = 0;
Time_t t;
return 0;
{
}
else
{
ftw = 0;
st = 0;
}
t = TMX_NOTIME;
{
{
return 0;
}
}
{
}
{
case KEY_atime:
if (st)
{
t = tmxgetatime(st);
}
if (!arg)
break;
case KEY_blocks:
if (st)
break;
case KEY_ctime:
if (st)
{
t = tmxgetctime(st);
}
if (!arg)
break;
case KEY_dev:
if (st)
break;
case KEY_device:
else
return 0;
break;
case KEY_devmajor:
if (st)
break;
case KEY_devminor:
if (st)
break;
case KEY_dir_blocks:
{
if (lp)
break;
}
/*FALLTHROUGH*/
case KEY_dir_bytes:
if (lp)
{
}
break;
case KEY_dir_count:
{
n = state.directories;
n = 1;
}
break;
case KEY_dir_files:
if (lp)
break;
case KEY_environ:
return 0;
break;
case KEY_flags:
break;
case KEY_gid:
if (st)
{
else
}
break;
case KEY_ino:
if (st)
break;
case KEY_linkpath:
{
char* dirnam;
int c;
static char* txtdata;
static int txtsize;
{
}
else
{
}
if (c > 0)
}
else
return 0;
break;
case KEY_linkop:
s = "->";
else
return 0;
break;
case KEY_mark:
if (!st)
return 0;
s = "@";
s = "/";
#ifdef S_ISDOOR
s = ">";
#endif
s = "|";
#ifdef S_ISSOCK
s = "=";
#endif
s = "$";
s = "*";
else
return 0;
break;
case KEY_markdir:
return 0;
s = "/";
break;
case KEY_mode:
if (st)
if (!arg)
break;
case KEY_mtime:
if (st)
{
t = tmxgetmtime(st);
}
if (!arg)
break;
case KEY_name:
if (ftw)
break;
case KEY_nlink:
if (st)
break;
case KEY_path:
if (ftw)
break;
case KEY_perm:
if (st)
if (!arg)
break;
case KEY_size:
if (st)
{
{
}
}
break;
case KEY_total_blocks:
{
break;
}
/*FALLTHROUGH*/
case KEY_total_bytes:
{
}
break;
case KEY_total_files:
break;
case KEY_uid:
if (st)
{
else
}
break;
case KEY_view:
if (st)
break;
default:
return 0;
}
if (s)
{
*ps = s;
if (mbwide())
{
register char* p;
int w;
int i;
for (p = s; w = mbchar(s); p = s)
if (w < 0)
s++;
else if ((i = mbwidth(w)) >= 0)
}
}
{
else
{
{
if (*arg == '=')
arg++;
}
if (!*arg)
{
t = TMX_NOTIME;
}
}
}
else
*pn = n;
return 1;
}
/*
* print info on a single file
* parent directory name is dirnam of dirlen chars
*/
static void
{
{
}
#ifdef S_ISLNK
/*
* -H == --hairbrained
* no way around it - this is bud tugley
* symlinks should be no more visible than mount points
* but I wear my user hat more than my administrator hat
*/
if (ftw->level == 0 && (state.ftwflags & (FTW_META|FTW_PHYSICAL)) == (FTW_META|FTW_PHYSICAL) && !(ftw->info & FTW_D) && !lstat(ftw->path ? ftw->path : ftw->name, &ftw->statb) && S_ISLNK(ftw->statb.st_mode))
#endif
{
if (fill > 0)
while (fill-- > 0)
else
}
}
/*
* pr() ftw directory child list in column order
* directory name is dirnam of dirlen chars
* count is the number of VISIBLE children
* length is the length of the longest VISIBLE child
*/
static void
{
register Ftw_t* p;
register int i;
register int n;
register int files;
register char* s;
int w;
int a;
{
{
n = w = 1;
a = 0;
}
else
{
length += w;
}
{
length = w - 1;
i = 0;
{
if (!mbwide())
w = p->namelen;
else
if (i < 0)
{
s++;
w++;
}
else if ((n = mbwidth(i)) > 0)
w += n;
w += a;
if ((n -= length + w) < 0)
{
if (i)
}
else if (i)
i = 1;
}
if (i)
}
else if (n <= 1)
{
}
else
{
register Ftw_t** x;
int c;
int j;
int k;
int l;
int m;
int o;
int q;
int r;
int w;
int z;
static unsigned short* siz;
static int sizsiz;
static int vecsiz;
{
}
{
}
x = vec;
i = 0;
x[i++] = p;
o = 0;
{
c = (i - 1) / n + 1;
do
{
w = -AFTER;
for (j = 0; j < c; j++)
{
z = 0;
for (l = 0, r = j; l < n && r < i; r += c, l++)
if (z < (x[r]->namelen + a))
z = x[r]->namelen + a;
w += z + BETWEEN;
}
o = n;
n = o ? o : 1;
c = (i - 1) / n + 1;
k = 0;
for (j = 0; j < c; j++)
{
siz[k] = 0;
for (l = 0, r = j; l < n && r < i; r += c, l++)
k++;
}
for (j = 0; j <= i; j += c)
for (l = 0, w = j; l < k && w < i; l++, w++)
}
else
{
o = 0;
if (n > 1)
{
if (!(q = i / n))
q = 1;
{
n = m = (i + c - 1) / c;
n -= (r + c - 1) / c;
for (; n <= m; n++)
{
w = -AFTER;
j = 0;
while (j < i)
{
z = 0;
for (l = 0; l < n && j < i; j++, l++)
if (z < x[j]->namelen)
z = x[j]->namelen;
w += z + a + BETWEEN;
}
{
q = c;
o = n;
break;
}
}
}
}
n = o ? o : 1;
j = k = 0;
while (j < i)
{
siz[k] = 0;
for (l = 0; l < n && j < i; j++, l++)
k++;
}
for (j = 0; j < n; j++)
for (l = 0, w = j; l < k && w < i; l++, w += n)
}
}
}
}
/*
* order() helpers
*/
static int
{
return 0;
}
static int
{
return 1;
return -1;
return 0;
}
static int
{
return 1;
return -1;
return 0;
}
static int
{
return 1;
return -1;
return 0;
}
static int
{
return 1;
return -1;
return 0;
}
static int
{
register int n;
char* x1;
char* x2;
if (x1)
{
if (x2)
else
n = 1;
}
else if (x2)
n = -1;
else
n = 0;
if (!n)
return n;
}
static int
{
}
static int
{
}
/*
* order child entries
*/
static int
{
int n;
{
{
return 1;
}
return -1;
}
}
/*
* list a directory and its children
*/
static void
{
register Ftw_t* p;
register int length;
int top = 0;
{
}
else
{
}
state.directories++;
else
length = 0;
{
{
top++;
}
else if (VISIBLE(p))
{
{
{
else
goto invisible;
}
}
else
{
}
}
else
{
}
}
if (top)
{
{
state.directories++;
}
else if (top > 1)
else
}
}
/*
* list info on a single file
*/
static int
{
{
return 0;
}
{
case FTW_NS:
break;
return 0;
case FTW_DC:
break;
return 0;
case FTW_DNR:
break;
return 0;
case FTW_D:
case FTW_DNX:
break;
{
return 0;
}
return 0;
}
{
}
return 0;
}
int
{
register int n;
register char* s;
char* e;
long lsflags;
int dump = 0;
static char fmt_color[] = "%(mode:case:d*:\\E[01;34m%(name)s\\E[0m:l*:\\E[01;36m%(name)s\\E[0m:*x*:\\E[01;32m%(name)s\\E[0m:*:%(name)s)s";
s++;
else
s = argv[0];
error_info.id = s;
if (streq(s, "lc"))
else if (streq(s, "ll"))
else if (streq(s, "lsr"))
else if (streq(s, "lsx"))
else if (isatty(1))
{
}
{
switch (n)
{
case 'a':
break;
case 'b':
break;
case 'c':
break;
case 'd':
break;
case 'e':
break;
case 'f':
break;
case 'g':
case 'O':
else
break;
case 'h':
break;
case 'i':
break;
case 'k':
break;
case 'l':
break;
case 'm':
break;
case 'n':
break;
case 'o':
case 'G':
else
break;
case 'p':
break;
case 'q':
break;
case 'r':
break;
case 's':
break;
case 't':
break;
case 'u':
break;
case 'w':
if (*e == 'x' || *e == 'X' || *e == '.' || *e == '+')
{
}
if (*e)
break;
case 'x':
break;
case 'y':
else
{
case 'a':
break;
case 'c':
break;
case 'f':
break;
case 'm':
break;
case 'n':
break;
case 's':
break;
case 't':
break;
case 'v':
break;
case 'x':
break;
}
break;
case 'z':
{
case -10:
break;
/*FALLTHROUGH*/
case 'i':
break;
case -11:
break;
/*FALLTHROUGH*/
case 'f':
break;
case 'l':
break;
case 'L':
break;
case -12:
if (strchr(s, '\n'))
{
/*
* gnu compatibility
*/
s = sfprints("%%Q\n%s\n", s);
if (!s || !(s = strdup(s)))
}
break;
}
break;
case 'A':
break;
case 'B':
break;
case 'C':
break;
case 'D':
*s++ = 0;
{
s = 0;
}
{
if (!s)
break;
}
{
stresc(s);
if (strmatch(s, "*:case:*"))
}
break;
case 'E':
break;
case 'F':
break;
case 'H':
break;
case 'I':
break;
case 'J':
{
case 'c':
break;
case 'e':
break;
case 'l':
break;
case 'q':
break;
case 's':
break;
case 'S':
break;
}
break;
case 'K':
break;
case 'L':
break;
case 'N':
break;
case 'P':
break;
case 'Q':
break;
case 'R':
break;
case 'S':
break;
case 'T':
/* ignored */
break;
case 'U':
break;
case 'V':
{
case 't':
if (!isatty(1))
break;
/*FALLTHROUGH*/
case 'a':
{
}
break;
}
break;
case 'W':
{
case 'a':
break;
case 'c':
break;
}
break;
case 'X':
break;
case 'Y':
{
case 'a':
break;
case 'c':
break;
case 'l':
break;
case 'v':
break;
case '1':
break;
}
break;
case 'Z':
break;
case '1':
break;
case -101:
break;
case -102:
break;
case -103:
dump = 1;
break;
case -104:
if (*e)
break;
case -105:
break;
case '?':
break;
case ':':
break;
default:
continue;
}
}
if (error_info.errors)
{
else
{
{
}
}
}
#if !_mem_d_fileno_dirent && !_mem_d_ino_dirent
#endif
{
}
{
{
sfprintf(fmt, " %%(%s)s ", (state.timeflags & LS_ATIME) ? "atime" : (state.timeflags & LS_CTIME) ? "ctime" : "mtime");
}
}
else
if (dump)
{
return 0;
}
/*
* do it
*/
if (argv[0])
{
if (argv[1])
}
else
return error_info.errors != 0;
}