Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/std/ls.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1989-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*
25
* ls -- list file status
26
*/
27
28
#define TIME_ISO "%Q/%m-%d+%H:%M/%Y-%m-%d /"
29
#define TIME_LONG_ISO "%_K"
30
#define TIME_FULL_ISO "%_EK"
31
#define TIME_LOCALE "%c"
32
33
static const char usage[] =
34
"[-?\n@(#)$Id: ls (AT&T Research) 2012-04-20 $\n]"
35
USAGE_LICENSE
36
"[+NAME?ls - list files and/or directories]"
37
"[+DESCRIPTION?For each directory argument \bls\b lists the contents; for each"
38
" file argument the name and requested information are listed."
39
" The directory \b.\b is assumed if no file arguments appear."
40
" The listing is sorted by file name by default, except that file"
41
" arguments are listed before directories.]"
42
"[+?Multi-column terminal output display width is determined by \bioctl\b(2)"
43
" and/or the \bCOLUMNS\b environment variable.]"
44
"[+?\bgetconf PATH_RESOLVE\b determines how symbolic links are handled. This"
45
" can be explicitly overridden by the \b--logical\b, \b--metaphysical\b,"
46
" and \b--physical\b options below. \bPATH_RESOLVE\b can be one of:]{"
47
" [+logical?Follow all symbolic links.]"
48
" [+metaphysical?Follow command argument symbolic links,"
49
" otherwise don't follow.]"
50
" [+physical?Don't follow symbolic links.]"
51
"}"
52
53
"[a:all?List entries starting with \b.\b; turns off \b--almost-all\b.]"
54
"[A:almost-all?List all entries but \b.\b and \b..\b; turns off \b--all\b.]"
55
"[b:escape?Print escapes for nongraphic characters.]"
56
"[B:ignore-backups?Do not list entries ending with ~.]"
57
"[c:ctime?Sort by change time; list ctime with \b--long\b.]"
58
"[C:multi-column?List entries by columns.]"
59
"[d:directory?List directory entries instead of contents.]"
60
"[D:define?Define \akey\a with optional \avalue\a. \avalue\a will be expanded"
61
" when \b%(\b\akey\a\b)\b is specified in \b--format\b. \akey\a may"
62
" override internal \b--format\b identifiers.]:[key[=value]]]"
63
"[e:long-iso|long-time?Equivalent to \b--long --time-style=long-iso\b.]"
64
"[E:full-iso|full-time?Equivalent to \b--long --time-style=full-iso\b.]"
65
"[f:force?Force each argument to be interpreted as a directory and list"
66
" the name found in each slot in the physical directory order. Turns"
67
" on \b-aU\b and turns off \b-lrst\b. The results are undefined for"
68
" non-directory arguments.]"
69
"[Z:format?Append to the listing format string. \aformat\a follows"
70
" \bprintf\b(3) conventions, except that \bsfio\b(3) inline ids"
71
" are used instead of arguments:"
72
" %[-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a[:\asubformat\a]])\achar\a."
73
" If \achar\a is \bs\b then the string form of the item is listed,"
74
" otherwise the corresponding numeric form is listed. \asubformat\a"
75
" overrides the default formatting for \aid\a. Supported \aid\as"
76
" and \asubformat\as are:]:[format]{"
77
" [+atime?access time]"
78
" [+blocks?size in blocks]"
79
" [+ctime?change time]"
80
" [+dev?major/minor device numbers]"
81
" [+device?major/minor device numbers if block or character special device]"
82
" [+devmajor?major device number]"
83
" [+devminor?minor device number]"
84
" [+dir.blocks?directory blocks]"
85
" [+dir.bytes?directory size in bytes]"
86
" [+dir.count?directory entry count]"
87
" [+dir.files?directory file count]"
88
" [+flags?command line flags in effect]"
89
" [+gid?group id]"
90
" [+header?listing header]"
91
" [+ino?serial number]"
92
" [+linkop?link operation: -> for symbolic, empty otherwise]"
93
" [+linkname?symbolic link text]"
94
" [+linkpath?symbolic link text]"
95
" [+mark?file or directory mark character]"
96
" [+markdir?directory mark character]"
97
" [+mode?access mode]"
98
" [+mtime?modification time]"
99
" [+name?entry name]"
100
" [+nlink?hard link count]"
101
" [+path?file path from original root dir]"
102
" [+perm?access permissions]"
103
" [+size?file size in bytes]"
104
" [+summary?listing summary info]"
105
" [+total.blocks?running total block count]"
106
" [+total.bytes?running total size in bytes]"
107
" [+total.files?running total file count]"
108
" [+trailer?listing trailer]"
109
" [+uid?owner id]"
110
" [+view?3d fs view level, 0 for the top or 2d]"
111
" [+----?subformats ----]"
112
" [+case\b::\bp\b\a1\a::\bs\b\a1\a::...::\bp\b\an\a::\bs\b\an\a?Expands"
113
" to \bs\b\ai\a if the value of \aid\a matches the shell"
114
" pattern \bp\b\ai\a, or the empty string if there is no"
115
" match.]"
116
" [+mode?The integral value as a \bfmtmode\b(3) string.]"
117
" [+perm?The integral value as a \bfmtperm\b(3) string.]"
118
" [+time[=\aformat\a]]?The integral value as a \bstrftime\b(3)"
119
" string. For example,"
120
" \b--format=\"%8(mtime)u %(ctime:time=%H:%M:%S)s\"\b"
121
" lists the mtime in seconds since the epoch and the"
122
" ctime as hours:minutes:seconds.]"
123
" }"
124
"[F:classify?Append a character for typing each entry. Turns on \b--physical\b.]"
125
"[g:group?\b--long\b with no owner info.]"
126
"[G?\b--long\b with no group info.]"
127
"[h:scale|binary-scale|human-readable?Scale sizes to powers of 1024 { Ki Mi Gi Ti Pi Xi }.]"
128
"[i:inode?List the file serial number.]"
129
"[I:ignore?Do not list implied entries matching shell \apattern\a.]:[pattern]"
130
"[k:kilobytes?Use 1024 blocks instead of 512.]"
131
"[K:shell-quote?Enclose entry names in shell $'...' if necessary.]"
132
"[l:long|verbose?Use a long listing format.]"
133
"[m:commas|comma-list?List names as comma separated list.]"
134
"[n:numeric-uid-gid?List numeric user and group ids instead of names.]"
135
"[N:literal|show-controls-chars?Print raw entry names (don't treat e.g. control characters specially).]"
136
"[o:owner?\b--long\b with no group info.]"
137
"[O?\b--long\b with no owner info.]"
138
"[p:markdir?Append / to each directory name.]"
139
"[q:hide-control-chars?Print ? instead of non graphic characters.]"
140
"[Q:quote-name?Enclose all entry names in \"...\".]"
141
"[J:quote-style|quoting-style?Quote entry names according to \astyle\a:]:[style:=question]{"
142
" [c:C?C \"...\" quote.]"
143
" [e:escape?\b\\\b escape if necessary.]"
144
" [l:literal?No quoting.]"
145
" [q:question?Replace unprintable characters with \b?\b.]"
146
" [s:shell?Shell $'...' quote if necessary.]"
147
" [S:shell-always?Shell $'...' every name.]"
148
"}"
149
"[r:reverse?Reverse order while sorting.]"
150
"[R:recursive?List subdirectories recursively.]"
151
"[s:size?Print size of each file, in blocks.]"
152
"[S:bysize?Sort by file size.]"
153
"[t:?Sort by modification time; list mtime with \b--long\b.]"
154
"[T:tabsize?Ignored by this implementation.]#[columns]"
155
"[u:access?Sort by last access time; list atime with \b--long\b.]"
156
"[U?Equivalent to \b--sort=none\b.]"
157
"[V:colors|colours?\akey\a determines when color is used to distinguish"
158
" types:]:?[key:=never]{"
159
" [n:never?Never use color.]"
160
" [a:always?Always use color.]"
161
" [t:tty|auto?Use color when output is a tty.]"
162
"}"
163
"[w:width?Set the screen width to \ascreen-width\a and the screen height"
164
" to \ascreen-height\a if specified.]:[[screen-heightX]]screen-width]"
165
"[W:time?Display \akey\a time instead of the modification time:]:[key]{"
166
" [a:atime|access|use?access time]"
167
" [c:ctime|status?status change time]"
168
" [m:mtime|time?modify time]"
169
"}"
170
"[x:across?List entries by lines instead of by columns.]"
171
"[X:extension?Sort alphabetically by entry extension.]"
172
"[y:sort?Sort by \akey\a:]:?[key]{"
173
" [a:atime|access|use?Access time.]"
174
" [c:ctime|status?Status change time.]"
175
" [x:extension?File name extension.]"
176
" [m:mtime|time?Modify time.]"
177
" [f:name?File name.]"
178
" [n:none?Don't sort.]"
179
" [s:size|blocks?File size.]"
180
" [v:version?File name version.]"
181
"}"
182
"[Y:layout?Listing layout \akey\a:]:[key]{"
183
" [a:across|horizontal?Multi-column across the page.]"
184
" [c:comma?Comma separated names across the page.]"
185
" [l:long|verbose?Long listing.]"
186
" [v:multi-column|vertical?Multi-column by column.]"
187
" [1:single-column?One column down the page.]"
188
"}"
189
"[z:time-style?List the time according to \astyle\a:]:[style]{"
190
" [i:iso?Equivalent to \b+" TIME_ISO "\b.]"
191
" [10:posix-iso?No change for the C or posix locales, \biso\b otherwise.]"
192
" [f:full-iso?Equivalent to \b+" TIME_FULL_ISO "\b.]"
193
" [l:long-iso?Equivalent to \b+" TIME_LONG_ISO "\b.]"
194
" [11:posix-full-iso?No change for the C or posix locales, \bfull-iso\b"
195
" otherwise.]"
196
" [L:locale?Equivalent to \b+" TIME_LOCALE "\b.]"
197
" [12:+\aformat\a?A \bdate\b(1) +\aformat\a.]"
198
"}"
199
"[1:one-column?List one file per line.]"
200
"[L:logical|follow?Follow symbolic links. The default is determined by"
201
" \bgetconf PATH_RESOLVE\b.]"
202
"[H:metaphysical?Follow command argument symbolic links, otherwise don't"
203
" follow. The default is determined by \bgetconf PATH_RESOLVE\b.]"
204
"[P:physical?Don't follow symbolic links. The default is determined by"
205
" \bgetconf PATH_RESOLVE\b.]"
206
"[101:block-size?Use \ablocksize\a blocks.]#[blocksize]"
207
"[102:decimal-scale|thousands?Scale sizes to powers of 1000 { K M G T P X }.]"
208
"[103:dump?Print the generated \b--format\b string on the standard output"
209
" and exit.]"
210
"[104:testdate?\b--format\b time values newer than \adate\a will be printed"
211
" as \adate\a. Used for regression testing.]:[date]"
212
"[105:testsize?Shift file sizes left \ashift\a bits and set file block counts"
213
" to the file size divided by 512. Used for regression testing.]#[shift]"
214
215
"\n"
216
"\n[ file ... ]\n"
217
"\n"
218
"[+SEE ALSO?\bchmod\b(1), \bfind\b(1), \bgetconf\b(1), \btw\b(1)]"
219
"[+BUGS?Can we add options to something else now?]"
220
;
221
222
#include <ast.h>
223
#include <ls.h>
224
#include <ctype.h>
225
#include <error.h>
226
#include <ftwalk.h>
227
#include <sfdisc.h>
228
#include <hash.h>
229
#include <tmx.h>
230
#include <fs3d.h>
231
232
#define LS_ACROSS (LS_USER<<0) /* multi-column row order */
233
#define LS_ALL (LS_USER<<1) /* list all */
234
#define LS_ALWAYS (LS_USER<<2) /* always quote */
235
#define LS_COLUMNS (LS_USER<<3) /* multi-column column order */
236
#define LS_COMMAS (LS_USER<<4) /* comma separated name list */
237
#define LS_DIRECTORY (LS_USER<<5) /* list directories as files */
238
#define LS_ESCAPE (LS_USER<<6) /* C escape unprintable chars */
239
#define LS_EXTENSION (LS_USER<<7) /* sort by name extension */
240
#define LS_LABEL (LS_USER<<8) /* label for all dirs */
241
#define LS_MARKDIR (LS_USER<<9) /* marks dirs with / */
242
#define LS_MOST (LS_USER<<10) /* list all but . and .. */
243
#define LS_NOBACKUP (LS_USER<<11) /* omit *~ names */
244
#define LS_NOSTAT (LS_USER<<13) /* leaf FTW_NS ok */
245
#define LS_PRINTABLE (LS_USER<<14) /* ? for non-printable chars */
246
#define LS_QUOTE (LS_USER<<15) /* "..." file names */
247
#define LS_RECURSIVE (LS_USER<<16) /* recursive directory descent */
248
#define LS_SEPARATE (LS_USER<<17) /* dir header needs separator */
249
#define LS_SHELL (LS_USER<<18) /* $'...' file names */
250
#define LS_TIME (LS_USER<<19) /* sort by time */
251
252
#define LS_STAT LS_NOSTAT
253
254
#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])))
255
256
#define BETWEEN 2 /* space between columns */
257
#define AFTER 1 /* space after last column */
258
259
#define INVISIBLE (-1)
260
#define LISTED (-2)
261
262
#define KEY_environ (-1)
263
264
#define KEY_atime 1
265
#define KEY_blocks 2
266
#define KEY_ctime 3
267
#define KEY_dev 4
268
#define KEY_device 5
269
#define KEY_devmajor 6
270
#define KEY_devminor 7
271
#define KEY_dir_blocks 8
272
#define KEY_dir_bytes 9
273
#define KEY_dir_count 10
274
#define KEY_dir_files 11
275
#define KEY_flags 12
276
#define KEY_gid 13
277
#define KEY_header 14
278
#define KEY_ino 15
279
#define KEY_linkop 16
280
#define KEY_linkpath 17
281
#define KEY_mark 18
282
#define KEY_markdir 19
283
#define KEY_mode 20
284
#define KEY_mtime 21
285
#define KEY_name 22
286
#define KEY_nlink 23
287
#define KEY_path 24
288
#define KEY_perm 25
289
#define KEY_size 26
290
#define KEY_summary 27
291
#define KEY_total_blocks 28
292
#define KEY_total_bytes 29
293
#define KEY_total_files 30
294
#define KEY_trailer 31
295
#define KEY_uid 32
296
#define KEY_view 33
297
298
#if 0
299
#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))
300
#else
301
#define BLOCKS(st) ((state.blocksize==LS_BLOCKSIZE)?iblocks(st):(iblocks(st)*LS_BLOCKSIZE+state.blocksize-1)/state.blocksize)
302
#endif
303
#define PRINTABLE(s) ((state.lsflags&LS_PRINTABLE)?printable(s):(s))
304
305
typedef int (*Order_f)(Ftw_t*, Ftw_t*);
306
307
typedef struct /* dir/total counts */
308
{
309
Sfulong_t blocks; /* number of blocks */
310
Sfulong_t bytes; /* number of bytes */
311
Sfulong_t files; /* number of files */
312
} Count_t;
313
314
typedef struct /* sfkeyprintf() keys */
315
{
316
char* name; /* key name */
317
short index; /* index */
318
short disable; /* macro being expanded */
319
char* macro; /* macro definition */
320
} Key_t;
321
322
typedef struct /* list state */
323
{
324
Count_t count; /* directory counts */
325
Ftw_t* ftw; /* ftw info */
326
char* dirnam; /* pr() dirnam */
327
int dirlen; /* pr() dirlen */
328
} List_t;
329
330
typedef struct /* program state */
331
{
332
char flags[64]; /* command line option flags */
333
long ftwflags; /* FTW_* flags */
334
long lsflags; /* LS_* flags */
335
long timeflags; /* time LS_* flags */
336
long blocksize; /* file block size */
337
unsigned long directories; /* directory count */
338
unsigned long testdate; /* --format test date */
339
Count_t total; /* total counts */
340
int adjust; /* key() print with adjustment */
341
int comma; /* LS_COMMAS ftw.level crossing */
342
int height; /* output height in lines */
343
int reverse; /* reverse the sort */
344
int scale; /* metric scale power */
345
int testsize; /* st_size left shift */
346
int width; /* output width in chars */
347
char* endflags; /* trailing 0 in flags */
348
char* format; /* sfkeyprintf() format */
349
char* ignore; /* ignore files matching this */
350
char* timefmt; /* time list format */
351
Hash_table_t* keys; /* sfkeyprintf() keys */
352
Sfio_t* tmp; /* tmp string stream */
353
Ftw_t* top; /* top directory -- no label */
354
Order_f order; /* sort comparison function */
355
} State_t;
356
357
static char DEF_header[] =
358
"%(dir.count:case;0;;1;%(path)s:\n;*;\n%(path)s:\n)s"
359
"%(flags:case;*d*;;*[ls]*;total %(dir.blocks)u\n)s"
360
;
361
362
static Key_t keys[] =
363
{
364
{ 0 },
365
{ "atime", KEY_atime },
366
{ "blocks", KEY_blocks },
367
{ "ctime", KEY_ctime },
368
{ "dev", KEY_dev },
369
{ "device", KEY_device },
370
{ "devmajor", KEY_devmajor },
371
{ "devminor", KEY_devminor },
372
{ "dir.blocks", KEY_dir_blocks },
373
{ "dir.bytes", KEY_dir_bytes },
374
{ "dir.count", KEY_dir_count },
375
{ "dir.files", KEY_dir_files },
376
{ "flags", KEY_flags },
377
{ "gid", KEY_gid },
378
{ "header", KEY_header, 0, DEF_header },
379
{ "ino", KEY_ino },
380
{ "linkop", KEY_linkop },
381
{ "linkpath", KEY_linkpath },
382
{ "mark", KEY_mark },
383
{ "markdir", KEY_markdir },
384
{ "mode", KEY_mode },
385
{ "mtime", KEY_mtime },
386
{ "name", KEY_name },
387
{ "nlink", KEY_nlink },
388
{ "path", KEY_path },
389
{ "perm", KEY_perm },
390
{ "size", KEY_size },
391
{ "summary", KEY_summary },
392
{ "total.blocks", KEY_total_blocks },
393
{ "total.bytes", KEY_total_bytes },
394
{ "total.files", KEY_total_files },
395
{ "trailer", KEY_trailer },
396
{ "uid", KEY_uid },
397
{ "view", KEY_view },
398
399
/* aliases */
400
401
{ "linkname", KEY_linkpath },
402
};
403
404
static State_t state;
405
406
/*
407
* return a copy of s with unprintable chars replaced by ?
408
*/
409
410
static char*
411
printable(register char* s)
412
{
413
register char* t;
414
register char* p;
415
register int c;
416
417
static char* prdata;
418
static int prsize;
419
420
if (state.lsflags & LS_ESCAPE)
421
{
422
if (!(state.lsflags & LS_QUOTE))
423
return fmtesc(s);
424
if (state.lsflags & LS_SHELL)
425
return fmtquote(s, "$'", "'", strlen(s), (state.lsflags & LS_ALWAYS) ? FMT_ALWAYS : 0);
426
return fmtquote(s, "\"", "\"", strlen(s), FMT_ALWAYS);
427
}
428
c = strlen(s) + 4;
429
if (c > prsize)
430
{
431
prsize = roundof(c, 512);
432
if (!(prdata = newof(prdata, char, prsize, 0)))
433
error(3, "out of space");
434
}
435
t = prdata;
436
if (state.lsflags & LS_QUOTE)
437
*t++ = '"';
438
if (!mbwide())
439
while (c = *s++)
440
*t++ = (iscntrl(c) || !isprint(c)) ? '?' : c;
441
else
442
for (p = s; c = mbchar(s);)
443
if (c < 0)
444
{
445
s++;
446
*t++ = '?';
447
}
448
else if (mbwidth(c) <= 0)
449
*t++ = '?';
450
else
451
while (p < s)
452
*t++ = *p++;
453
if (state.lsflags & LS_QUOTE)
454
*t++ = '"';
455
*t = 0;
456
return prdata;
457
}
458
459
/*
460
* sfkeyprintf() lookup
461
*/
462
463
static int
464
key(void* handle, register Sffmt_t* fp, const char* arg, char** ps, Sflong_t* pn)
465
{
466
register Ftw_t* ftw;
467
register struct stat* st;
468
register char* s = 0;
469
register Sflong_t n = 0;
470
register Key_t* kp;
471
List_t* lp;
472
Time_t t;
473
474
static Sfio_t* mp;
475
static const char fmt_mode[] = "mode";
476
static const char fmt_perm[] = "perm";
477
static const char fmt_time[] = "time";
478
479
if (!fp->t_str)
480
return 0;
481
if (lp = (List_t*)handle)
482
{
483
ftw = lp->ftw;
484
st = &ftw->statb;
485
}
486
else
487
{
488
ftw = 0;
489
st = 0;
490
}
491
t = TMX_NOTIME;
492
if (!(kp = (Key_t*)hashget(state.keys, fp->t_str)))
493
{
494
if (*fp->t_str != '$')
495
{
496
error(3, "%s: unknown format key", fp->t_str);
497
return 0;
498
}
499
if (!(kp = newof(0, Key_t, 1, 0)))
500
error(3, "out of space");
501
kp->name = hashput(state.keys, 0, kp);
502
kp->macro = getenv(fp->t_str + 1);
503
kp->index = KEY_environ;
504
kp->disable = 1;
505
}
506
if (kp->macro && !kp->disable)
507
{
508
kp->disable = 1;
509
if (!mp && !(mp = sfstropen()))
510
error(3, "out of space");
511
sfkeyprintf(mp, handle, kp->macro, key, NiL);
512
if (!(s = sfstruse(mp)))
513
error(3, "out of space");
514
kp->disable = 0;
515
}
516
else switch (kp->index)
517
{
518
case KEY_atime:
519
if (st)
520
{
521
n = st->st_atime;
522
t = tmxgetatime(st);
523
}
524
if (!arg)
525
arg = state.timefmt;
526
break;
527
case KEY_blocks:
528
if (st)
529
n = BLOCKS(st);
530
break;
531
case KEY_ctime:
532
if (st)
533
{
534
n = st->st_ctime;
535
t = tmxgetctime(st);
536
}
537
if (!arg)
538
arg = state.timefmt;
539
break;
540
case KEY_dev:
541
if (st)
542
s = fmtdev(st);
543
break;
544
case KEY_device:
545
if (st && (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)))
546
s = fmtdev(st);
547
else
548
return 0;
549
break;
550
case KEY_devmajor:
551
if (st)
552
n = (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) ? major(idevice(st)) : major(st->st_dev);
553
break;
554
case KEY_devminor:
555
if (st)
556
n = (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) ? minor(idevice(st)) : minor(st->st_dev);
557
break;
558
case KEY_dir_blocks:
559
if (!state.scale)
560
{
561
if (lp)
562
n = lp->count.blocks;
563
break;
564
}
565
/*FALLTHROUGH*/
566
case KEY_dir_bytes:
567
if (lp)
568
n = lp->count.bytes;
569
if (state.scale)
570
{
571
s = fmtscale(n, state.scale);
572
fp->fmt = 's';
573
}
574
break;
575
case KEY_dir_count:
576
if (ftw != state.top)
577
{
578
if (state.lsflags & LS_SEPARATE)
579
n = state.directories;
580
else if (state.lsflags & LS_LABEL)
581
n = 1;
582
}
583
break;
584
case KEY_dir_files:
585
if (lp)
586
n = lp->count.files;
587
break;
588
case KEY_environ:
589
if (!(s = kp->macro))
590
return 0;
591
break;
592
case KEY_flags:
593
s = state.flags;
594
break;
595
case KEY_gid:
596
if (st)
597
{
598
if (fp->fmt == 's')
599
s = fmtgid(st->st_gid);
600
else
601
n = st->st_gid;
602
}
603
break;
604
case KEY_ino:
605
if (st)
606
n = st->st_ino;
607
break;
608
case KEY_linkpath:
609
if (ftw && (ftw->info & FTW_SL))
610
{
611
char* dirnam;
612
int c;
613
614
static char* txtdata;
615
static int txtsize;
616
617
if ((st->st_size + 1) > txtsize)
618
{
619
txtsize = roundof(st->st_size + 1, 512);
620
if (!(txtdata = newof(txtdata, char, txtsize, 0)))
621
error(3, "out of space");
622
}
623
if (*ftw->name == '/' || !lp->dirnam)
624
dirnam = ftw->name;
625
else
626
{
627
sfprintf(state.tmp, "%s/%s", lp->dirnam + streq(lp->dirnam, "/"), ftw->name);
628
if (!(dirnam = sfstruse(state.tmp)))
629
error(3, "out of space");
630
}
631
c = pathgetlink(dirnam, txtdata, txtsize);
632
if (c > 0)
633
s = PRINTABLE(txtdata);
634
}
635
else
636
return 0;
637
break;
638
case KEY_linkop:
639
if (ftw && (ftw->info & FTW_SL))
640
s = "->";
641
else
642
return 0;
643
break;
644
case KEY_mark:
645
if (!st)
646
return 0;
647
else if (S_ISLNK(st->st_mode))
648
s = "@";
649
else if (S_ISDIR(st->st_mode))
650
s = "/";
651
#ifdef S_ISDOOR
652
else if (S_ISDOOR(st->st_mode))
653
s = ">";
654
#endif
655
else if (S_ISFIFO(st->st_mode))
656
s = "|";
657
#ifdef S_ISSOCK
658
else if (S_ISSOCK(st->st_mode))
659
s = "=";
660
#endif
661
else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode))
662
s = "$";
663
else if (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
664
s = "*";
665
else
666
return 0;
667
break;
668
case KEY_markdir:
669
if (!st || !S_ISDIR(st->st_mode))
670
return 0;
671
s = "/";
672
break;
673
case KEY_mode:
674
if (st)
675
n = st->st_mode;
676
if (!arg)
677
arg = fmt_mode;
678
break;
679
case KEY_mtime:
680
if (st)
681
{
682
n = st->st_mtime;
683
t = tmxgetmtime(st);
684
}
685
if (!arg)
686
arg = state.timefmt;
687
break;
688
case KEY_name:
689
if (ftw)
690
s = PRINTABLE(ftw->name);
691
break;
692
case KEY_nlink:
693
if (st)
694
n = st->st_nlink;
695
break;
696
case KEY_path:
697
if (ftw)
698
s = ftw->path ? PRINTABLE(ftw->path) : PRINTABLE(ftw->name);
699
break;
700
case KEY_perm:
701
if (st)
702
n = st->st_mode & S_IPERM;
703
if (!arg)
704
arg = fmt_perm;
705
break;
706
case KEY_size:
707
if (st)
708
{
709
n = st->st_size;
710
if (state.scale)
711
{
712
s = fmtscale(n, state.scale);
713
fp->fmt = 's';
714
}
715
}
716
break;
717
case KEY_total_blocks:
718
if (!state.scale)
719
{
720
n = state.total.blocks;
721
break;
722
}
723
/*FALLTHROUGH*/
724
case KEY_total_bytes:
725
n = state.total.bytes;
726
if (state.scale)
727
{
728
s = fmtscale(n, state.scale);
729
fp->fmt = 's';
730
}
731
break;
732
case KEY_total_files:
733
n = state.total.files;
734
break;
735
case KEY_uid:
736
if (st)
737
{
738
if (fp->fmt == 's')
739
s = fmtuid(st->st_uid);
740
else
741
n = st->st_uid;
742
}
743
break;
744
case KEY_view:
745
if (st)
746
n = iview(st);
747
break;
748
default:
749
return 0;
750
}
751
if (s)
752
{
753
*ps = s;
754
if (mbwide())
755
{
756
register char* p;
757
int w;
758
int i;
759
760
for (p = s; w = mbchar(s); p = s)
761
if (w < 0)
762
s++;
763
else if ((i = mbwidth(w)) >= 0)
764
state.adjust -= (s - p) + i - 2;
765
}
766
}
767
else if (fp->fmt == 's' && arg)
768
{
769
if (strneq(arg, fmt_mode, sizeof(fmt_mode) - 1))
770
*ps = fmtmode(n, 0);
771
else if (strneq(arg, fmt_perm, sizeof(fmt_perm) - 1))
772
*ps = fmtperm(n & S_IPERM);
773
else
774
{
775
if (strneq(arg, fmt_time, sizeof(fmt_time) - 1))
776
{
777
arg += sizeof(fmt_time) - 1;
778
if (*arg == '=')
779
arg++;
780
}
781
if (!*arg)
782
arg = state.timefmt;
783
if ((unsigned long)n >= state.testdate)
784
{
785
n = state.testdate;
786
t = TMX_NOTIME;
787
}
788
*ps = t == TMX_NOTIME ? fmttime(arg, (time_t)n) : fmttmx(arg, t);
789
}
790
}
791
else
792
*pn = n;
793
return 1;
794
}
795
796
/*
797
* print info on a single file
798
* parent directory name is dirnam of dirlen chars
799
*/
800
801
static void
802
pr(register List_t* lp, Ftw_t* ftw, register int fill)
803
{
804
if (state.testsize)
805
{
806
ftw->statb.st_size <<= state.testsize;
807
ftw->statb.st_blocks = ftw->statb.st_size / LS_BLOCKSIZE;
808
}
809
#ifdef S_ISLNK
810
/*
811
* -H == --hairbrained
812
* no way around it - this is bud tugley
813
* symlinks should be no more visible than mount points
814
* but I wear my user hat more than my administrator hat
815
*/
816
817
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))
818
ftw->info = FTW_SL;
819
#endif
820
lp->ftw = ftw;
821
state.adjust = 0;
822
fill -= sfkeyprintf(sfstdout, lp, state.format, key, NiL) + state.adjust;
823
if (!(state.lsflags & LS_COMMAS))
824
{
825
if (fill > 0)
826
while (fill-- > 0)
827
sfputc(sfstdout, ' ');
828
else
829
sfputc(sfstdout, '\n');
830
}
831
}
832
833
/*
834
* pr() ftw directory child list in column order
835
* directory name is dirnam of dirlen chars
836
* count is the number of VISIBLE children
837
* length is the length of the longest VISIBLE child
838
*/
839
840
static void
841
col(register List_t* lp, register Ftw_t* ftw, int length)
842
{
843
register Ftw_t* p;
844
register int i;
845
register int n;
846
register int files;
847
register char* s;
848
int w;
849
int a;
850
851
lp->ftw = ftw;
852
if (keys[KEY_header].macro && ftw->level >= 0)
853
sfkeyprintf(sfstdout, lp, keys[KEY_header].macro, key, NiL);
854
if ((files = lp->count.files) > 0)
855
{
856
if (!(state.lsflags & LS_COLUMNS) || length <= 0)
857
{
858
n = w = 1;
859
a = 0;
860
}
861
else
862
{
863
i = ftw->name[1];
864
ftw->name[1] = 0;
865
state.adjust = 2;
866
a = sfkeyprintf(state.tmp, lp, state.format, key, NiL) - 1;
867
w = a + state.adjust + 1;
868
length += w;
869
sfstrseek(state.tmp, 0, SEEK_SET);
870
ftw->name[1] = i;
871
n = ((state.width - (length + BETWEEN + 2)) < 0) ? 1 : 2;
872
}
873
if (state.lsflags & LS_COMMAS)
874
{
875
length = w - 1;
876
i = 0;
877
n = state.width;
878
for (p = ftw->link; p; p = p->link)
879
if (p->local.number != INVISIBLE)
880
{
881
if (!mbwide())
882
w = p->namelen;
883
else
884
for (s = p->name, w = 0; i = mbchar(s);)
885
if (i < 0)
886
{
887
s++;
888
w++;
889
}
890
else if ((n = mbwidth(i)) > 0)
891
w += n;
892
w += a;
893
if ((n -= length + w) < 0)
894
{
895
n = state.width - (length + w);
896
if (i)
897
sfputr(sfstdout, ",\n", -1);
898
}
899
else if (i)
900
sfputr(sfstdout, ", ", -1);
901
pr(lp, p, 0);
902
i = 1;
903
}
904
if (i)
905
sfputc(sfstdout, '\n');
906
}
907
else if (n <= 1)
908
{
909
for (p = ftw->link; p; p = p->link)
910
if (p->local.number != INVISIBLE)
911
pr(lp, p, 0);
912
}
913
else
914
{
915
register Ftw_t** x;
916
int c;
917
int j;
918
int k;
919
int l;
920
int m;
921
int o;
922
int q;
923
int r;
924
int w;
925
int z;
926
927
static unsigned short* siz;
928
static int sizsiz;
929
930
static Ftw_t** vec;
931
static int vecsiz;
932
933
if (files > sizsiz)
934
{
935
sizsiz = roundof(files, 64);
936
if (!(siz = newof(siz, unsigned short, sizsiz, 0)))
937
error(3, "out of space");
938
}
939
if (files > (vecsiz - 1))
940
{
941
vecsiz = roundof(files + 1, 64);
942
if (!(vec = newof(vec, Ftw_t*, vecsiz, 0)))
943
error(3, "out of space");
944
}
945
x = vec;
946
i = 0;
947
for (p = ftw->link; p; p = p->link)
948
if (p->local.number != INVISIBLE)
949
x[i++] = p;
950
n = i / (state.width / (length + BETWEEN)) + 1;
951
o = 0;
952
if ((state.lsflags & LS_ACROSS) && n > 1)
953
{
954
c = (i - 1) / n + 1;
955
do
956
{
957
w = -AFTER;
958
for (j = 0; j < c; j++)
959
{
960
z = 0;
961
for (l = 0, r = j; l < n && r < i; r += c, l++)
962
if (z < (x[r]->namelen + a))
963
z = x[r]->namelen + a;
964
w += z + BETWEEN;
965
}
966
if (w <= state.width)
967
o = n;
968
} while (c < state.width / 2 && (n = (i + c) / (c + 1)) && ++c);
969
n = o ? o : 1;
970
c = (i - 1) / n + 1;
971
k = 0;
972
for (j = 0; j < c; j++)
973
{
974
siz[k] = 0;
975
for (l = 0, r = j; l < n && r < i; r += c, l++)
976
if (siz[k] < x[r]->namelen)
977
siz[k] = x[r]->namelen;
978
siz[k] += a + BETWEEN;
979
k++;
980
}
981
for (j = 0; j <= i; j += c)
982
for (l = 0, w = j; l < k && w < i; l++, w++)
983
pr(lp, x[w], l < (k - 1) && w < (i - 1) ? siz[l] : 0);
984
}
985
else
986
{
987
o = 0;
988
if (n > 1)
989
{
990
if (!(q = i / n))
991
q = 1;
992
for (c = q; (c - q) < 2 && c <= state.width / (BETWEEN + 1); ++c)
993
{
994
n = m = (i + c - 1) / c;
995
if ((r = i - m * c) > state.height)
996
n -= (r + c - 1) / c;
997
for (; n <= m; n++)
998
{
999
w = -AFTER;
1000
j = 0;
1001
while (j < i)
1002
{
1003
z = 0;
1004
for (l = 0; l < n && j < i; j++, l++)
1005
if (z < x[j]->namelen)
1006
z = x[j]->namelen;
1007
w += z + a + BETWEEN;
1008
}
1009
if (w <= state.width)
1010
{
1011
q = c;
1012
o = n;
1013
break;
1014
}
1015
}
1016
}
1017
}
1018
n = o ? o : 1;
1019
j = k = 0;
1020
while (j < i)
1021
{
1022
siz[k] = 0;
1023
for (l = 0; l < n && j < i; j++, l++)
1024
if (siz[k] < x[j]->namelen)
1025
siz[k] = x[j]->namelen;
1026
siz[k] += a + BETWEEN;
1027
k++;
1028
}
1029
for (j = 0; j < n; j++)
1030
for (l = 0, w = j; l < k && w < i; l++, w += n)
1031
pr(lp, x[w], l < (k - 1) && w < (i - n) ? siz[l] : 0);
1032
}
1033
}
1034
}
1035
if (keys[KEY_trailer].macro && ftw->level >= 0)
1036
sfkeyprintf(sfstdout, lp, keys[KEY_trailer].macro, key, NiL);
1037
}
1038
1039
/*
1040
* order() helpers
1041
*/
1042
1043
static int
1044
order_none(register Ftw_t* f1, register Ftw_t* f2)
1045
{
1046
return 0;
1047
}
1048
1049
static int
1050
order_blocks(register Ftw_t* f1, register Ftw_t* f2)
1051
{
1052
if (f1->statb.st_size < f2->statb.st_size)
1053
return 1;
1054
if (f1->statb.st_size > f2->statb.st_size)
1055
return -1;
1056
return 0;
1057
}
1058
1059
static int
1060
order_atime(register Ftw_t* f1, register Ftw_t* f2)
1061
{
1062
Time_t t1;
1063
Time_t t2;
1064
1065
t1 = tmxgetatime(&f1->statb);
1066
t2 = tmxgetatime(&f2->statb);
1067
if (t1 < t2)
1068
return 1;
1069
if (t1 > t2)
1070
return -1;
1071
return 0;
1072
}
1073
1074
static int
1075
order_ctime(register Ftw_t* f1, register Ftw_t* f2)
1076
{
1077
Time_t t1;
1078
Time_t t2;
1079
1080
t1 = tmxgetctime(&f1->statb);
1081
t2 = tmxgetctime(&f2->statb);
1082
if (t1 < t2)
1083
return 1;
1084
if (t1 > t2)
1085
return -1;
1086
return 0;
1087
}
1088
1089
static int
1090
order_mtime(register Ftw_t* f1, register Ftw_t* f2)
1091
{
1092
Time_t t1;
1093
Time_t t2;
1094
1095
t1 = tmxgetmtime(&f1->statb);
1096
t2 = tmxgetmtime(&f2->statb);
1097
if (t1 < t2)
1098
return 1;
1099
if (t1 > t2)
1100
return -1;
1101
return 0;
1102
}
1103
1104
static int
1105
order_extension(register Ftw_t* f1, register Ftw_t* f2)
1106
{
1107
register int n;
1108
char* x1;
1109
char* x2;
1110
1111
x1 = strrchr(f1->name, '.');
1112
x2 = strrchr(f2->name, '.');
1113
if (x1)
1114
{
1115
if (x2)
1116
n = strcoll(x1, x2);
1117
else
1118
n = 1;
1119
}
1120
else if (x2)
1121
n = -1;
1122
else
1123
n = 0;
1124
if (!n)
1125
n = strcoll(f1->name, f2->name);
1126
return n;
1127
}
1128
1129
static int
1130
order_version(Ftw_t* f1, Ftw_t* f2)
1131
{
1132
return strvcmp(f1->name, f2->name);
1133
}
1134
1135
static int
1136
order_name(Ftw_t* f1, Ftw_t* f2)
1137
{
1138
return strcoll(f1->name, f2->name);
1139
}
1140
1141
/*
1142
* order child entries
1143
*/
1144
1145
static int
1146
order(register Ftw_t* f1, register Ftw_t* f2)
1147
{
1148
int n;
1149
1150
if (!(state.lsflags & LS_DIRECTORY) && (state.ftwflags & FTW_MULTIPLE) && f1->level == 0)
1151
{
1152
if (f1->info == FTW_D)
1153
{
1154
if (f2->info != FTW_D)
1155
return 1;
1156
}
1157
else if (f2->info == FTW_D)
1158
return -1;
1159
}
1160
n = (*state.order)(f1, f2);
1161
return state.reverse ? -n : n;
1162
}
1163
1164
/*
1165
* list a directory and its children
1166
*/
1167
1168
static void
1169
dir(register Ftw_t* ftw)
1170
{
1171
register Ftw_t* p;
1172
register int length;
1173
int top = 0;
1174
List_t list;
1175
1176
if (ftw->status == FTW_NAME)
1177
{
1178
list.dirlen = ftw->namelen;
1179
list.dirnam = ftw->path + ftw->pathlen - list.dirlen;
1180
}
1181
else
1182
{
1183
list.dirlen = ftw->pathlen;
1184
list.dirnam = ftw->path;
1185
}
1186
if (ftw->level >= 0)
1187
state.directories++;
1188
else
1189
state.top = ftw;
1190
length = 0;
1191
list.count.blocks = 0;
1192
list.count.bytes = 0;
1193
list.count.files = 0;
1194
for (p = ftw->link; p; p = p->link)
1195
{
1196
if (p->level == 0 && p->info == FTW_D && !(state.lsflags & LS_DIRECTORY))
1197
{
1198
p->local.number = INVISIBLE;
1199
top++;
1200
}
1201
else if (VISIBLE(p))
1202
{
1203
if (p->info == FTW_NS)
1204
{
1205
if (ftw->level < 0 || !(state.lsflags & LS_NOSTAT))
1206
{
1207
if (ftw->path[0] == '.' && !ftw->path[1])
1208
error(2, "%s: not found", p->name);
1209
else
1210
error(2, "%s/%s: not found", ftw->path, p->name);
1211
goto invisible;
1212
}
1213
}
1214
else
1215
{
1216
list.count.blocks += BLOCKS(&p->statb);
1217
list.count.bytes += p->statb.st_size;
1218
}
1219
list.count.files++;
1220
if (p->namelen > length)
1221
length = p->namelen;
1222
if (!(state.lsflags & LS_RECURSIVE))
1223
p->status = FTW_SKIP;
1224
}
1225
else
1226
{
1227
invisible:
1228
p->local.number = INVISIBLE;
1229
p->status = FTW_SKIP;
1230
}
1231
}
1232
state.total.blocks += list.count.blocks;
1233
state.total.bytes += list.count.bytes;
1234
state.total.files += list.count.files;
1235
col(&list, ftw, length);
1236
state.lsflags |= LS_SEPARATE;
1237
if (top)
1238
{
1239
if (list.count.files)
1240
{
1241
state.directories++;
1242
state.top = 0;
1243
}
1244
else if (top > 1)
1245
state.top = 0;
1246
else
1247
state.top = ftw->link;
1248
for (p = ftw->link; p; p = p->link)
1249
if (p->level == 0 && p->info == FTW_D)
1250
p->local.number = 0;
1251
}
1252
}
1253
1254
/*
1255
* list info on a single file
1256
*/
1257
1258
static int
1259
ls(register Ftw_t* ftw)
1260
{
1261
if (!VISIBLE(ftw))
1262
{
1263
ftw->status = FTW_SKIP;
1264
return 0;
1265
}
1266
switch (ftw->info)
1267
{
1268
case FTW_NS:
1269
if (ftw->parent->info == FTW_DNX)
1270
break;
1271
error(2, "%s: not found", ftw->path);
1272
return 0;
1273
case FTW_DC:
1274
if (state.lsflags & LS_DIRECTORY)
1275
break;
1276
error(2, "%s: directory causes cycle", ftw->path);
1277
return 0;
1278
case FTW_DNR:
1279
if (state.lsflags & LS_DIRECTORY)
1280
break;
1281
error(2, "%s: cannot read directory", ftw->path);
1282
return 0;
1283
case FTW_D:
1284
case FTW_DNX:
1285
if ((state.lsflags & LS_DIRECTORY) && ftw->level >= 0)
1286
break;
1287
if (!(state.lsflags & LS_RECURSIVE))
1288
ftw->status = FTW_SKIP;
1289
else if (ftw->info == FTS_DNX)
1290
{
1291
error(2, "%s: cannot search directory", ftw->path, ftw->level);
1292
ftw->status = FTW_SKIP;
1293
if (ftw->level > 0 && !(state.lsflags & LS_NOSTAT))
1294
return 0;
1295
}
1296
dir(ftw);
1297
return 0;
1298
}
1299
ftw->status = FTW_SKIP;
1300
if (!ftw->level)
1301
{
1302
static List_t list;
1303
1304
list.ftw = ftw;
1305
pr(&list, ftw, 0);
1306
}
1307
return 0;
1308
}
1309
1310
#define set(f) (opt_info.num?(state.lsflags|=(f)):((state.lsflags&=~(f)),0))
1311
#define clr(f) (opt_info.num?(state.lsflags&=~(f)):(state.lsflags|=(f)))
1312
1313
int
1314
main(int argc, register char** argv)
1315
{
1316
register int n;
1317
register char* s;
1318
char* e;
1319
Key_t* kp;
1320
Sfio_t* fmt;
1321
long lsflags;
1322
int dump = 0;
1323
1324
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";
1325
1326
NoP(argc);
1327
setlocale(LC_ALL, "");
1328
if (s = strrchr(argv[0], '/'))
1329
s++;
1330
else
1331
s = argv[0];
1332
error_info.id = s;
1333
state.ftwflags = ftwflags() | FTW_META | FTW_CHILDREN;
1334
if (!(fmt = sfstropen()) || !(state.tmp = sfstropen()))
1335
error(3, "out of space");
1336
if (!(state.keys = hashalloc(NiL, HASH_name, "keys", 0)))
1337
error(3, "out of space");
1338
for (n = 1; n < elementsof(keys); n++)
1339
hashput(state.keys, keys[n].name, &keys[keys[n].index]);
1340
hashset(state.keys, HASH_ALLOCATE);
1341
if (streq(s, "lc"))
1342
state.lsflags |= LS_COLUMNS;
1343
else if (streq(s, "lf") || streq(s, "lsf"))
1344
state.lsflags |= LS_MARK;
1345
else if (streq(s, "ll"))
1346
state.lsflags |= LS_LONG;
1347
else if (streq(s, "lsr"))
1348
state.lsflags |= LS_RECURSIVE;
1349
else if (streq(s, "lsx"))
1350
state.lsflags |= LS_ACROSS|LS_COLUMNS;
1351
else if (isatty(1))
1352
{
1353
state.lsflags |= LS_COLUMNS;
1354
if (!strmatch(setlocale(LC_ALL, NiL), "*[Uu][Tt][Ff]?(-)8"))
1355
state.lsflags |= LS_PRINTABLE;
1356
}
1357
state.endflags = state.flags;
1358
state.blocksize = 512;
1359
state.testdate = ~0;
1360
state.timefmt = "%?%l";
1361
lsflags = state.lsflags;
1362
while (n = optget(argv, usage))
1363
{
1364
switch (n)
1365
{
1366
case 'a':
1367
set(LS_ALL);
1368
break;
1369
case 'b':
1370
set(LS_PRINTABLE|LS_ESCAPE);
1371
break;
1372
case 'c':
1373
state.lsflags &= ~LS_ATIME;
1374
state.lsflags |= LS_CTIME;
1375
if (!state.order)
1376
state.order = order_ctime;
1377
break;
1378
case 'd':
1379
set(LS_DIRECTORY);
1380
break;
1381
case 'e':
1382
state.lsflags |= LS_LONG;
1383
state.timefmt = TIME_LONG_ISO;
1384
break;
1385
case 'f':
1386
state.lsflags |= LS_ALL;
1387
state.lsflags &= ~(LS_BLOCKS|LS_LONG|LS_TIME);
1388
state.reverse = 0;
1389
state.order = order_none;
1390
break;
1391
case 'g':
1392
case 'O':
1393
if (opt_info.num)
1394
state.lsflags |= LS_LONG|LS_NOUSER;
1395
else
1396
state.lsflags |= LS_LONG|LS_NOGROUP;
1397
break;
1398
case 'h':
1399
state.scale = 1024;
1400
break;
1401
case 'i':
1402
set(LS_INUMBER);
1403
break;
1404
case 'k':
1405
state.blocksize = 1024;
1406
break;
1407
case 'l':
1408
set(LS_LONG);
1409
break;
1410
case 'm':
1411
set(LS_COMMAS);
1412
break;
1413
case 'n':
1414
set(LS_NUMBER);
1415
break;
1416
case 'o':
1417
case 'G':
1418
if (opt_info.num)
1419
state.lsflags |= LS_LONG|LS_NOGROUP;
1420
else
1421
state.lsflags |= LS_LONG|LS_NOUSER;
1422
break;
1423
case 'p':
1424
set(LS_MARKDIR);
1425
break;
1426
case 'q':
1427
set(LS_PRINTABLE);
1428
break;
1429
case 'r':
1430
state.reverse = !!opt_info.num;
1431
break;
1432
case 's':
1433
set(LS_BLOCKS);
1434
break;
1435
case 't':
1436
if (set(LS_TIME) && !state.order)
1437
state.order = order_mtime;
1438
break;
1439
case 'u':
1440
state.lsflags &= ~LS_CTIME;
1441
state.lsflags |= LS_ATIME;
1442
if (!state.order)
1443
state.order = order_atime;
1444
break;
1445
case 'w':
1446
state.width = strtol(opt_info.arg, &e, 0);
1447
if (*e == 'x' || *e == 'X' || *e == '.' || *e == '+')
1448
{
1449
state.height = state.width;
1450
state.width = strtol(e + 1, &e, 0);
1451
}
1452
if (*e)
1453
error(2, "%s: invalid screen width specification at `%s'", opt_info.arg, e);
1454
break;
1455
case 'x':
1456
set(LS_ACROSS|LS_COLUMNS);
1457
break;
1458
case 'y':
1459
if (!opt_info.arg)
1460
state.order = order_none;
1461
else
1462
switch (opt_info.num)
1463
{
1464
case 'a':
1465
state.order = order_atime;
1466
break;
1467
case 'c':
1468
state.order = order_ctime;
1469
break;
1470
case 'f':
1471
state.order = 0;
1472
break;
1473
case 'm':
1474
state.order = order_mtime;
1475
break;
1476
case 'n':
1477
state.order = order_none;
1478
break;
1479
case 's':
1480
state.order = order_blocks;
1481
break;
1482
case 't':
1483
state.order = order_mtime;
1484
break;
1485
case 'v':
1486
state.order = order_version;
1487
break;
1488
case 'x':
1489
state.order = order_extension;
1490
break;
1491
}
1492
break;
1493
case 'z':
1494
switch (opt_info.num)
1495
{
1496
case -10:
1497
if (!strcmp(setlocale(LC_TIME, NiL), "C"))
1498
break;
1499
/*FALLTHROUGH*/
1500
case 'i':
1501
state.timefmt = TIME_ISO;
1502
break;
1503
case -11:
1504
if (!strcmp(setlocale(LC_TIME, NiL), "C"))
1505
break;
1506
/*FALLTHROUGH*/
1507
case 'f':
1508
state.timefmt = TIME_FULL_ISO;
1509
break;
1510
case 'l':
1511
state.timefmt = TIME_LONG_ISO;
1512
break;
1513
case 'L':
1514
state.timefmt = TIME_LOCALE;
1515
break;
1516
case -12:
1517
s = opt_info.arg + 1;
1518
if (strchr(s, '\n'))
1519
{
1520
/*
1521
* gnu compatibility
1522
*/
1523
1524
s = sfprints("%%Q\n%s\n", s);
1525
if (!s || !(s = strdup(s)))
1526
error(ERROR_SYSTEM|3, "out of space");
1527
}
1528
state.timefmt = s;
1529
break;
1530
}
1531
break;
1532
case 'A':
1533
state.lsflags |= LS_MOST;
1534
state.lsflags &= ~LS_ALL;
1535
break;
1536
case 'B':
1537
set(LS_NOBACKUP);
1538
break;
1539
case 'C':
1540
set(LS_COLUMNS);
1541
break;
1542
case 'D':
1543
if (s = strchr(opt_info.arg, '='))
1544
*s++ = 0;
1545
if (*opt_info.arg == 'n' && *(opt_info.arg + 1) == 'o')
1546
{
1547
opt_info.arg += 2;
1548
s = 0;
1549
}
1550
if (!(kp = (Key_t*)hashget(state.keys, opt_info.arg)))
1551
{
1552
if (!s)
1553
break;
1554
if (!(kp = newof(0, Key_t, 1, 0)))
1555
error(3, "out of space");
1556
kp->name = hashput(state.keys, 0, kp);
1557
}
1558
if (kp->macro = s)
1559
{
1560
stresc(s);
1561
if (strmatch(s, "*:case:*"))
1562
state.lsflags |= LS_STAT;
1563
}
1564
break;
1565
case 'E':
1566
state.lsflags |= LS_LONG;
1567
state.timefmt = TIME_FULL_ISO;
1568
break;
1569
case 'F':
1570
set(LS_MARK);
1571
break;
1572
case 'H':
1573
state.ftwflags |= FTW_META|FTW_PHYSICAL;
1574
break;
1575
case 'I':
1576
state.ignore = opt_info.arg;
1577
break;
1578
case 'J':
1579
state.lsflags &= ~(LS_ALWAYS|LS_ESCAPE|LS_PRINTABLE|LS_QUOTE|LS_SHELL);
1580
switch (opt_info.num)
1581
{
1582
case 'c':
1583
state.lsflags |= LS_ESCAPE|LS_PRINTABLE|LS_QUOTE;
1584
break;
1585
case 'e':
1586
state.lsflags |= LS_ESCAPE|LS_PRINTABLE;
1587
break;
1588
case 'l':
1589
break;
1590
case 'q':
1591
state.lsflags |= LS_PRINTABLE;
1592
break;
1593
case 's':
1594
state.lsflags |= LS_ESCAPE|LS_PRINTABLE|LS_QUOTE|LS_SHELL;
1595
break;
1596
case 'S':
1597
state.lsflags |= LS_ALWAYS|LS_ESCAPE|LS_PRINTABLE|LS_QUOTE|LS_SHELL;
1598
break;
1599
}
1600
break;
1601
case 'K':
1602
set(LS_PRINTABLE|LS_SHELL|LS_QUOTE|LS_ESCAPE);
1603
break;
1604
case 'L':
1605
state.ftwflags &= ~(FTW_META|FTW_PHYSICAL|FTW_SEEDOTDIR);
1606
break;
1607
case 'N':
1608
clr(LS_PRINTABLE);
1609
break;
1610
case 'P':
1611
state.ftwflags &= ~FTW_META;
1612
state.ftwflags |= FTW_PHYSICAL;
1613
break;
1614
case 'Q':
1615
set(LS_PRINTABLE|LS_QUOTE);
1616
break;
1617
case 'R':
1618
set(LS_RECURSIVE);
1619
break;
1620
case 'S':
1621
state.order = order_blocks;
1622
break;
1623
case 'T':
1624
/* ignored */
1625
break;
1626
case 'U':
1627
state.order = order_none;
1628
break;
1629
case 'V':
1630
switch (opt_info.num)
1631
{
1632
case 't':
1633
if (!isatty(1))
1634
break;
1635
/*FALLTHROUGH*/
1636
case 'a':
1637
if (kp = (Key_t*)hashget(state.keys, "name"))
1638
{
1639
stresc(kp->macro = fmt_color);
1640
state.lsflags |= LS_STAT;
1641
}
1642
break;
1643
}
1644
break;
1645
case 'W':
1646
state.timeflags = 0;
1647
switch (opt_info.num)
1648
{
1649
case 'a':
1650
state.timeflags = LS_ATIME;
1651
break;
1652
case 'c':
1653
state.timeflags = LS_CTIME;
1654
break;
1655
}
1656
break;
1657
case 'X':
1658
set(LS_EXTENSION);
1659
break;
1660
case 'Y':
1661
switch (opt_info.num)
1662
{
1663
case 'a':
1664
state.lsflags |= LS_ACROSS|LS_COLUMNS;
1665
break;
1666
case 'c':
1667
state.lsflags |= LS_COMMAS;
1668
break;
1669
case 'l':
1670
state.lsflags |= LS_LONG;
1671
break;
1672
case 'v':
1673
state.lsflags &= ~LS_ACROSS;
1674
state.lsflags |= LS_COLUMNS;
1675
break;
1676
case '1':
1677
state.lsflags &= ~(LS_ACROSS|LS_COLUMNS);
1678
break;
1679
}
1680
break;
1681
case 'Z':
1682
if (!sfstrtell(fmt))
1683
state.lsflags &= ~LS_COLUMNS;
1684
sfputr(fmt, opt_info.arg, ' ');
1685
break;
1686
case '1':
1687
clr(LS_COLUMNS|LS_PRINTABLE);
1688
break;
1689
case -101:
1690
if (opt_info.num <= 0)
1691
error(3, "%ld: invalid block size", opt_info.num);
1692
state.blocksize = opt_info.num;
1693
break;
1694
case -102:
1695
state.scale = 1000;
1696
break;
1697
case -103:
1698
dump = 1;
1699
break;
1700
case -104:
1701
state.testdate = tmdate(opt_info.arg, &e, NiL);
1702
if (*e)
1703
error(3, "%s: invalid date string", opt_info.arg, opt_info.option);
1704
break;
1705
case -105:
1706
state.testsize = opt_info.num;
1707
break;
1708
case '?':
1709
error(ERROR_USAGE|4, "%s", opt_info.arg);
1710
break;
1711
case ':':
1712
error(2, "%s", opt_info.arg);
1713
break;
1714
default:
1715
error(1, "%s: option not implemented", opt_info.name);
1716
continue;
1717
}
1718
if (!strchr(state.flags, n))
1719
*state.endflags++ = n;
1720
}
1721
argv += opt_info.index;
1722
if (error_info.errors)
1723
error(ERROR_USAGE|4, "%s", optusage(NiL));
1724
if (state.lsflags == (lsflags|LS_TIME))
1725
state.ftwflags |= FTW_SEEDOTDIR; /* keep configure happy */
1726
if (state.lsflags & LS_DIRECTORY)
1727
state.lsflags &= ~LS_RECURSIVE;
1728
if (!state.order)
1729
state.order = order_name;
1730
if (!state.timeflags)
1731
state.timeflags = state.lsflags;
1732
if (state.lsflags & (LS_COLUMNS|LS_COMMAS))
1733
{
1734
if (state.lsflags & LS_LONG)
1735
state.lsflags &= ~(LS_COLUMNS|LS_COMMAS);
1736
else
1737
{
1738
if (!state.width)
1739
{
1740
astwinsize(1, &state.height, &state.width);
1741
if (state.width <= 20)
1742
state.width = 80;
1743
}
1744
if (state.height <= 4)
1745
state.height = 24;
1746
}
1747
}
1748
if (state.lsflags & LS_STAT)
1749
state.lsflags &= ~LS_NOSTAT;
1750
else if (!(state.lsflags & (LS_DIRECTORY|LS_BLOCKS|LS_LONG|LS_MARK|LS_MARKDIR|LS_TIME
1751
#if !_mem_d_fileno_dirent && !_mem_d_ino_dirent
1752
|LS_INUMBER
1753
#endif
1754
)) && !sfstrtell(fmt))
1755
{
1756
state.lsflags |= LS_NOSTAT;
1757
state.ftwflags |= FTW_DELAY|FTW_DOT;
1758
}
1759
if (!sfstrtell(fmt))
1760
{
1761
if (state.lsflags & LS_INUMBER)
1762
sfputr(fmt, "%6(ino)u ", -1);
1763
if (state.lsflags & LS_BLOCKS)
1764
sfputr(fmt, "%5(blocks)u ", -1);
1765
if (state.lsflags & LS_LONG)
1766
{
1767
sfputr(fmt, "%(mode)s %3(nlink)u", -1);
1768
if (!(state.lsflags & LS_NOUSER))
1769
sfprintf(fmt, " %%-8(uid)%c", (state.lsflags & LS_NUMBER) ? 'd' : 's');
1770
if (!(state.lsflags & LS_NOGROUP))
1771
sfprintf(fmt, " %%-8(gid)%c", (state.lsflags & LS_NUMBER) ? 'd' : 's');
1772
sfputr(fmt, " %8(device:case::%(size)u:*:%(device)s)s", -1);
1773
sfprintf(fmt, " %%(%s)s ", (state.timeflags & LS_ATIME) ? "atime" : (state.timeflags & LS_CTIME) ? "ctime" : "mtime");
1774
}
1775
sfputr(fmt, "%(name)s", -1);
1776
if (state.lsflags & LS_MARK)
1777
sfputr(fmt, "%(mark)s", -1);
1778
else if (state.lsflags & LS_MARKDIR)
1779
sfputr(fmt, "%(markdir)s", -1);
1780
if (state.lsflags & LS_LONG)
1781
sfputr(fmt, "%(linkop:case:?*: %(linkop)s %(linkpath)s)s", -1);
1782
}
1783
else
1784
sfstrseek(fmt, -1, SEEK_CUR);
1785
if (!(state.format = sfstruse(fmt)))
1786
error(3, "out of space");
1787
if (dump)
1788
{
1789
sfprintf(sfstdout, "%s\n", state.format);
1790
return 0;
1791
}
1792
stresc(state.format);
1793
1794
/*
1795
* do it
1796
*/
1797
1798
if (argv[0])
1799
{
1800
if (argv[1])
1801
state.lsflags |= LS_LABEL;
1802
state.ftwflags |= FTW_MULTIPLE;
1803
ftwalk((char*)argv, ls, state.ftwflags, order);
1804
}
1805
else
1806
ftwalk(".", ls, state.ftwflags, order);
1807
if (keys[KEY_summary].macro)
1808
sfkeyprintf(sfstdout, NiL, keys[KEY_summary].macro, key, NiL);
1809
return error_info.errors != 0;
1810
}
1811
1812