VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTLs.cpp@ 69614

最後變更 在這個檔案從69614是 69614,由 vboxsync 提交於 7 年 前

RTLs: Quickly hacked up human readable size, owners and groups. Stack usage isn't optimal here...

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 71.0 KB
 
1/* $Id: RTLs.cpp 69614 2017-11-08 12:58:04Z vboxsync $ */
2/** @file
3 * IPRT - /bin/ls like utility for testing the VFS code.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/vfs.h>
32
33#include <iprt/buildconfig.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/initterm.h>
37#include <iprt/mem.h>
38#include <iprt/message.h>
39#include <iprt/param.h>
40#include <iprt/path.h>
41#include <iprt/sort.h>
42#include <iprt/stream.h>
43#include <iprt/string.h>
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49/**
50 * Display entry.
51 */
52typedef struct RTCMDLSENTRY
53{
54 /** The information about the entry. */
55 RTFSOBJINFO Info;
56 /** Symbolic link target (allocated after the name). */
57 const char *pszTarget;
58 /** Owner if applicable(allocated after the name). */
59 const char *pszOwner;
60 /** Group if applicable (allocated after the name). */
61 const char *pszGroup;
62 /** The length of szName. */
63 size_t cchName;
64 /** The entry name. */
65 char szName[RT_FLEXIBLE_ARRAY];
66} RTCMDLSENTRY;
67/** Pointer to a ls display entry. */
68typedef RTCMDLSENTRY *PRTCMDLSENTRY;
69/** Pointer to a ls display entry pointer. */
70typedef PRTCMDLSENTRY *PPRTCMDLSENTRY;
71
72
73/**
74 * Collection of display entries.
75 */
76typedef struct RTCMDLSCOLLECTION
77{
78 /** Current size of papEntries. */
79 size_t cEntries;
80 /** Memory allocated for papEntries. */
81 size_t cEntriesAllocated;
82 /** Current entries pending sorting and display. */
83 PPRTCMDLSENTRY papEntries;
84
85 /** Total number of bytes allocated for the above entries. */
86 uint64_t cbTotalAllocated;
87 /** Total number of file content bytes. */
88 uint64_t cbTotalFiles;
89
90 /** The collection name (path). */
91 char szName[RT_FLEXIBLE_ARRAY];
92} RTCMDLSCOLLECTION;
93/** Pointer to a display entry collection. */
94typedef RTCMDLSCOLLECTION *PRTCMDLSCOLLECTION;
95/** Pointer to a display entry collection pointer. */
96typedef PRTCMDLSCOLLECTION *PPRTCMDLSCOLLECTION;
97
98
99/** Sorting. */
100typedef enum RTCMDLSSORT
101{
102 RTCMDLSSORT_INVALID = 0,
103 RTCMDLSSORT_NONE,
104 RTCMDLSSORT_NAME,
105 RTCMDLSSORT_EXTENSION,
106 RTCMDLSSORT_SIZE,
107 RTCMDLSSORT_TIME,
108 RTCMDLSSORT_VERSION
109} RTCMDLSSORT;
110
111/** Time selection. */
112typedef enum RTCMDLSTIME
113{
114 RTCMDLSTIME_INVALID = 0,
115 RTCMDLSTIME_BTIME,
116 RTCMDLSTIME_CTIME,
117 RTCMDLSTIME_MTIME,
118 RTCMDLSTIME_ATIME
119} RTCMDLSTIME;
120
121/** Time display style. */
122typedef enum RTCMDLSTIMESTYLE
123{
124 RTCMDLSTIMESTYLE_INVALID = 0,
125 RTCMDLSTIMESTYLE_FULL_ISO,
126 RTCMDLSTIMESTYLE_LONG_ISO,
127 RTCMDLSTIMESTYLE_ISO,
128 RTCMDLSTIMESTYLE_LOCALE,
129 RTCMDLSTIMESTYLE_CUSTOM
130} RTCMDLSTIMESTYLE;
131
132/** Coloring selection. */
133typedef enum RTCMDLSCOLOR
134{
135 RTCMDLSCOLOR_INVALID = 0,
136 RTCMDLSCOLOR_NONE
137} RTCMDLSCOLOR;
138
139/** Formatting. */
140typedef enum RTCMDLSFORMAT
141{
142 RTCMDLSFORMAT_INVALID = 0,
143 RTCMDLSFORMAT_COLS_VERTICAL, /**< -C/default */
144 RTCMDLSFORMAT_COLS_HORIZONTAL, /**< -x */
145 RTCMDLSFORMAT_COMMAS, /**< -m */
146 RTCMDLSFORMAT_SINGLE, /**< -1 */
147 RTCMDLSFORMAT_LONG, /**< -l */
148 RTCMDLSFORMAT_MACHINE_READABLE /**< --machine-readable */
149} RTCMDLSFORMAT;
150
151
152/**
153 * LS command options and state.
154 */
155typedef struct RTCMDLSOPTS
156{
157 /** @name Traversal.
158 * @{ */
159 bool fFollowSymlinksInDirs; /**< -L */
160 bool fFollowSymlinkToAnyArgs;
161 bool fFollowSymlinkToDirArgs;
162 bool fFollowDirectoryArgs; /**< Inverse -d/--directory. */
163 bool fRecursive; /**< -R */
164 /** @} */
165
166
167 /** @name Filtering.
168 * @{ */
169 bool fShowHidden; /**< -a/--all or -A/--almost-all */
170 bool fShowDotAndDotDot; /**< -a vs -A */
171 bool fShowBackups; /**< Inverse -B/--ignore-backups (*~). */
172 /** @} */
173
174 /** @name Sorting
175 * @{ */
176 RTCMDLSSORT enmSort; /**< --sort */
177 bool fReverseSort; /**< -r */
178 bool fGroupDirectoriesFirst; /**< fGroupDirectoriesFirst */
179 /** @} */
180
181 /** @name Formatting
182 * @{ */
183 RTCMDLSFORMAT enmFormat; /**< --format */
184
185 bool fEscapeNonGraphicChars; /**< -b, --escape */
186 bool fEscapeControlChars;
187 bool fHideControlChars; /**< -q/--hide-control-chars, --show-control-chars */
188
189 bool fHumanReadableSizes; /**< -h */
190 bool fSiUnits; /**< --si */
191 uint32_t cbBlock; /**< --block-size=N, -k */
192
193 bool fShowOwner;
194 bool fShowGroup;
195 bool fNumericalIds; /**< -n */
196 bool fShowINode;
197 bool fShowAllocatedSize; /**< -s */
198 uint8_t cchTab; /**< -T */
199 uint32_t cchWidth; /**< -w */
200
201 RTCMDLSCOLOR enmColor; /**< --color */
202
203 RTCMDLSTIME enmTime; /**< --time */
204 RTCMDLSTIMESTYLE enmTimeStyle; /**< --time-style, --full-time */
205 const char *pszTimeCustom; /**< --time-style=+xxx */
206 /** @} */
207
208 /** @name State
209 * @{ */
210 /** Current size of papCollections. */
211 size_t cCollections;
212 /** Memory allocated for papCollections. */
213 size_t cCollectionsAllocated;
214 /** Current entry collection pending display, the last may also be pending
215 * sorting. */
216 PPRTCMDLSCOLLECTION papCollections;
217 /** @} */
218} RTCMDLSOPTS;
219/** Pointer to ls options and state. */
220typedef RTCMDLSOPTS *PRTCMDLSOPTS;
221
222
223
224
225/** @callback_method_impl{FNRTSORTCMP, Dirs first + Unsorted} */
226static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstUnsorted(void const *pvElement1, void const *pvElement2, void *pvUser)
227{
228 RT_NOREF(pvUser);
229 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
230 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
231 return !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
232}
233
234
235/** @callback_method_impl{FNRTSORTCMP, Name} */
236static DECLCALLBACK(int) rtCmdLsEntryCmpName(void const *pvElement1, void const *pvElement2, void *pvUser)
237{
238 RT_NOREF(pvUser);
239 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
240 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
241 return RTStrCmp(pEntry1->szName, pEntry2->szName);
242}
243
244
245/** @callback_method_impl{FNRTSORTCMP, Dirs first + Name} */
246static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstName(void const *pvElement1, void const *pvElement2, void *pvUser)
247{
248 RT_NOREF(pvUser);
249 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
250 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
251 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
252 if (!iDiff)
253 iDiff = rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
254 return iDiff;
255}
256
257
258/** @callback_method_impl{FNRTSORTCMP, extension} */
259static DECLCALLBACK(int) rtCmdLsEntryCmpExtension(void const *pvElement1, void const *pvElement2, void *pvUser)
260{
261 RT_NOREF(pvUser);
262 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
263 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
264 int iDiff = RTStrCmp(RTPathSuffix(pEntry1->szName), RTPathSuffix(pEntry2->szName));
265 if (!iDiff)
266 iDiff = RTStrCmp(pEntry1->szName, pEntry2->szName);
267 return iDiff;
268}
269
270
271/** @callback_method_impl{FNRTSORTCMP, Dirs first + Ext + Name} */
272static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstExtension(void const *pvElement1, void const *pvElement2, void *pvUser)
273{
274 RT_NOREF(pvUser);
275 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
276 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
277 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
278 if (!iDiff)
279 iDiff = rtCmdLsEntryCmpExtension(pEntry1, pEntry2, pvUser);
280 return iDiff;
281}
282
283
284/** @callback_method_impl{FNRTSORTCMP, Allocated size + Name} */
285static DECLCALLBACK(int) rtCmdLsEntryCmpAllocated(void const *pvElement1, void const *pvElement2, void *pvUser)
286{
287 RT_NOREF(pvUser);
288 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
289 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
290 if (pEntry1->Info.cbAllocated == pEntry2->Info.cbAllocated)
291 return rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
292 return pEntry1->Info.cbAllocated < pEntry2->Info.cbAllocated ? -1 : 1;
293}
294
295
296/** @callback_method_impl{FNRTSORTCMP, Dirs first + Allocated size + Name} */
297static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstAllocated(void const *pvElement1, void const *pvElement2, void *pvUser)
298{
299 RT_NOREF(pvUser);
300 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
301 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
302 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
303 if (!iDiff)
304 iDiff = rtCmdLsEntryCmpAllocated(pEntry1, pEntry2, pvUser);
305 return iDiff;
306}
307
308
309/** @callback_method_impl{FNRTSORTCMP, Content size + Name} */
310static DECLCALLBACK(int) rtCmdLsEntryCmpSize(void const *pvElement1, void const *pvElement2, void *pvUser)
311{
312 RT_NOREF(pvUser);
313 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
314 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
315 if (pEntry1->Info.cbObject == pEntry2->Info.cbObject)
316 return rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
317 return pEntry1->Info.cbObject < pEntry2->Info.cbObject ? -1 : 1;
318}
319
320
321/** @callback_method_impl{FNRTSORTCMP, Dirs first + Content size + Name} */
322static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstSize(void const *pvElement1, void const *pvElement2, void *pvUser)
323{
324 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
325 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
326 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
327 if (!iDiff)
328 iDiff = rtCmdLsEntryCmpSize(pEntry1, pEntry2, pvUser);
329 return iDiff;
330}
331
332
333/** @callback_method_impl{FNRTSORTCMP, Modification time + name} */
334static DECLCALLBACK(int) rtCmdLsEntryCmpMTime(void const *pvElement1, void const *pvElement2, void *pvUser)
335{
336 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
337 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
338 int iDiff = RTTimeSpecCompare(&pEntry1->Info.ModificationTime, &pEntry2->Info.ModificationTime);
339 if (!iDiff)
340 iDiff = rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
341 return iDiff;
342}
343
344
345/** @callback_method_impl{FNRTSORTCMP, Dirs first + Modification time + Name} */
346static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstMTime(void const *pvElement1, void const *pvElement2, void *pvUser)
347{
348 RT_NOREF(pvUser);
349 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
350 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
351 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
352 if (!iDiff)
353 iDiff = rtCmdLsEntryCmpMTime(pEntry1, pEntry2, pvUser);
354 return iDiff;
355}
356
357
358/** @callback_method_impl{FNRTSORTCMP, Birth time + name} */
359static DECLCALLBACK(int) rtCmdLsEntryCmpBTime(void const *pvElement1, void const *pvElement2, void *pvUser)
360{
361 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
362 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
363 int iDiff = RTTimeSpecCompare(&pEntry1->Info.BirthTime, &pEntry2->Info.BirthTime);
364 if (!iDiff)
365 iDiff = rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
366 return iDiff;
367}
368
369
370/** @callback_method_impl{FNRTSORTCMP, Dirs first + Birth time + Name} */
371static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstBTime(void const *pvElement1, void const *pvElement2, void *pvUser)
372{
373 RT_NOREF(pvUser);
374 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
375 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
376 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
377 if (!iDiff)
378 iDiff = rtCmdLsEntryCmpBTime(pEntry1, pEntry2, pvUser);
379 return iDiff;
380}
381
382
383/** @callback_method_impl{FNRTSORTCMP, Change time + name} */
384static DECLCALLBACK(int) rtCmdLsEntryCmpCTime(void const *pvElement1, void const *pvElement2, void *pvUser)
385{
386 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
387 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
388 int iDiff = RTTimeSpecCompare(&pEntry1->Info.ChangeTime, &pEntry2->Info.ChangeTime);
389 if (!iDiff)
390 iDiff = rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
391 return iDiff;
392}
393
394
395/** @callback_method_impl{FNRTSORTCMP, Dirs first + Change time + Name} */
396static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstCTime(void const *pvElement1, void const *pvElement2, void *pvUser)
397{
398 RT_NOREF(pvUser);
399 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
400 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
401 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
402 if (!iDiff)
403 iDiff = rtCmdLsEntryCmpCTime(pEntry1, pEntry2, pvUser);
404 return iDiff;
405}
406
407
408/** @callback_method_impl{FNRTSORTCMP, Accessed time + name} */
409static DECLCALLBACK(int) rtCmdLsEntryCmpATime(void const *pvElement1, void const *pvElement2, void *pvUser)
410{
411 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
412 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
413 int iDiff = RTTimeSpecCompare(&pEntry1->Info.AccessTime, &pEntry2->Info.AccessTime);
414 if (!iDiff)
415 iDiff = rtCmdLsEntryCmpName(pEntry1, pEntry2, pvUser);
416 return iDiff;
417}
418
419
420/** @callback_method_impl{FNRTSORTCMP, Dirs first + Accessed time + Name} */
421static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstATime(void const *pvElement1, void const *pvElement2, void *pvUser)
422{
423 RT_NOREF(pvUser);
424 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
425 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
426 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
427 if (!iDiff)
428 iDiff = rtCmdLsEntryCmpATime(pEntry1, pEntry2, pvUser);
429 return iDiff;
430}
431
432
433/** @callback_method_impl{FNRTSORTCMP, Name as version} */
434static DECLCALLBACK(int) rtCmdLsEntryCmpVersion(void const *pvElement1, void const *pvElement2, void *pvUser)
435{
436 RT_NOREF(pvUser);
437 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
438 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
439 return RTStrVersionCompare(pEntry1->szName, pEntry2->szName);
440}
441
442
443/** @callback_method_impl{FNRTSORTCMP, Dirs first + Name as version} */
444static DECLCALLBACK(int) rtCmdLsEntryCmpDirFirstVersion(void const *pvElement1, void const *pvElement2, void *pvUser)
445{
446 RT_NOREF(pvUser);
447 PRTCMDLSENTRY pEntry1 = (PRTCMDLSENTRY)pvElement1;
448 PRTCMDLSENTRY pEntry2 = (PRTCMDLSENTRY)pvElement2;
449 int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
450 if (!iDiff)
451 iDiff = rtCmdLsEntryCmpVersion(pEntry1, pEntry2, pvUser);
452 return iDiff;
453}
454
455
456/**
457 * Sorts the entries in the collections according the sorting options.
458 *
459 * @param pOpts The options and state.
460 */
461static void rtCmdLsSortCollections(PRTCMDLSOPTS pOpts)
462{
463 /*
464 * Sort the entries in each collection.
465 */
466 PFNRTSORTCMP pfnCmp;
467 switch (pOpts->enmSort)
468 {
469 case RTCMDLSSORT_NONE:
470 pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstUnsorted : NULL;
471 break;
472 default: AssertFailed(); RT_FALL_THRU();
473 case RTCMDLSSORT_NAME:
474 pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstName : rtCmdLsEntryCmpName;
475 break;
476 case RTCMDLSSORT_EXTENSION:
477 pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstExtension : rtCmdLsEntryCmpExtension;
478 break;
479 case RTCMDLSSORT_SIZE:
480 if (pOpts->fShowAllocatedSize)
481 pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstAllocated : rtCmdLsEntryCmpAllocated;
482 else
483 pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstSize : rtCmdLsEntryCmpSize;
484 break;
485 case RTCMDLSSORT_TIME:
486 switch (pOpts->enmTime)
487 {
488 default: AssertFailed(); RT_FALL_THRU();
489 case RTCMDLSTIME_MTIME: pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstMTime : rtCmdLsEntryCmpMTime; break;
490 case RTCMDLSTIME_BTIME: pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstBTime : rtCmdLsEntryCmpBTime; break;
491 case RTCMDLSTIME_CTIME: pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstCTime : rtCmdLsEntryCmpCTime; break;
492 case RTCMDLSTIME_ATIME: pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstATime : rtCmdLsEntryCmpATime; break;
493 }
494 case RTCMDLSSORT_VERSION:
495 pfnCmp = pOpts->fGroupDirectoriesFirst ? rtCmdLsEntryCmpDirFirstVersion : rtCmdLsEntryCmpVersion;
496 break;
497 }
498 if (pfnCmp)
499 {
500 /*
501 * Walk thru the collections and sort their entries.
502 */
503 size_t i = pOpts->cCollections;
504 while (i-- > 0)
505 {
506 PRTCMDLSCOLLECTION pCollection = pOpts->papCollections[i];
507 RTSortApvShell((void **)pCollection->papEntries, pCollection->cEntries, pfnCmp, NULL);
508
509 if (pOpts->fReverseSort)
510 {
511 PPRTCMDLSENTRY papEntries = pCollection->papEntries;
512 size_t iHead = 0;
513 size_t iTail = pCollection->cEntries;
514 while (iHead < iTail)
515 {
516 PRTCMDLSENTRY pTmp = papEntries[iHead];
517 papEntries[iHead] = papEntries[iTail];
518 papEntries[iTail] = pTmp;
519 iHead++;
520 iTail--;
521 }
522 }
523 }
524 }
525
526 /** @todo sort the collections too, except for the first one. */
527}
528
529
530/**
531 * Format human readable size.
532 */
533static const char *rtCmdLsFormatSizeHumanReadable(PRTCMDLSOPTS pOpts, uint64_t cb, char *pszDst, size_t cbDst)
534{
535 if (pOpts->fHumanReadableSizes)
536 {
537 static const struct
538 {
539 const char *pszSuffix;
540 uint64_t cbFactor;
541 uint64_t cbMin;
542 } s_aUnits[] =
543 {
544 { "E", _1E, _1E + _1E/2 },
545 { "P", _1P, _1P + _1P/2 },
546 { "T", _1T, _1T + _1T/2 },
547 { "G", _1G, _1G + _1G/2 },
548 { "M", _1M, _1M + _1M/2 },
549 { "k", _1K, _1K + _1K/2 },
550
551 { "E", UINT64_C(1000000000000000000), UINT64_C(1010000000000000000), },
552 { "P", UINT64_C(1000000000000000), UINT64_C(1010000000000000), },
553 { "T", UINT64_C(1000000000000), UINT64_C(1010000000000), },
554 { "G", UINT64_C(1000000000), UINT64_C(1010000000), },
555 { "M", UINT64_C(1000000), UINT64_C(1010000), },
556 { "K", UINT64_C(1000), UINT64_C(1010), },
557 };
558 unsigned const iEnd = !pOpts->fSiUnits ? 6 : 12;
559 for (unsigned i = !pOpts->fSiUnits ? 0 : 6; i < iEnd; i++)
560 if (cb >= s_aUnits[i].cbMin)
561 {
562 RTStrFormatU64(pszDst, cbDst, cb / s_aUnits[i].cbFactor, 10, 0, 0, 0);
563 RTStrCat(pszDst, cbDst, s_aUnits[i].pszSuffix);
564 return pszDst;
565 }
566 }
567 else if (pOpts->cbBlock)
568 RTStrFormatU64(pszDst, cbDst, (cb + pOpts->cbBlock - 1) / pOpts->cbBlock, 10, 0, 0, 0);
569 else
570 RTStrFormatU64(pszDst, cbDst, cb, 10, 0, 0, 0);
571 return pszDst;
572}
573
574
575/**
576 * Format block count.
577 */
578static const char *rtCmdLsFormatBlocks(PRTCMDLSOPTS pOpts, uint64_t cb, char *pszDst, size_t cbDst)
579{
580 if (pOpts->fHumanReadableSizes)
581 return rtCmdLsFormatSizeHumanReadable(pOpts, cb, pszDst, cbDst);
582
583 uint32_t cbBlock = pOpts->cbBlock;
584 if (cbBlock == 0)
585 cbBlock = _1K;
586 RTStrFormatU64(pszDst, cbDst, (cb + cbBlock / 2 - 1) / cbBlock, 10, 0, 0, 0);
587 return pszDst;
588}
589
590
591/**
592 * Format file size.
593 */
594static const char *rtCmdLsFormatSize(PRTCMDLSOPTS pOpts, uint64_t cb, char *pszDst, size_t cbDst)
595{
596 if (pOpts->fHumanReadableSizes)
597 return rtCmdLsFormatSizeHumanReadable(pOpts, cb, pszDst, cbDst);
598 if (pOpts->cbBlock > 0)
599 return rtCmdLsFormatBlocks(pOpts, cb, pszDst, cbDst);
600 RTStrFormatU64(pszDst, cbDst, cb, 10, 0, 0, 0);
601 return pszDst;
602}
603
604
605/**
606 * Format name, i.e. escape, hide, quote stuff.
607 */
608static const char *rtCmdLsFormatName(PRTCMDLSOPTS pOpts, const char *pszName, char *pszDst, size_t cbDst)
609{
610 if ( !pOpts->fEscapeNonGraphicChars
611 && !pOpts->fEscapeControlChars
612 && !pOpts->fHideControlChars)
613 return pszName;
614 /** @todo implement name formatting. */
615 RT_NOREF(pszDst, cbDst);
616 return pszName;
617}
618
619
620/**
621 * Figures out the length for a 32-bit number when formatted as decimal.
622 * @returns Number of digits.
623 * @param uValue The number.
624 */
625DECLINLINE(size_t) rtCmdLsDecimalFormatLengthU32(uint32_t uValue)
626{
627 if (uValue < 10)
628 return 1;
629 if (uValue < 100)
630 return 2;
631 if (uValue < 1000)
632 return 3;
633 if (uValue < 10000)
634 return 4;
635 if (uValue < 100000)
636 return 5;
637 if (uValue < 1000000)
638 return 6;
639 if (uValue < 10000000)
640 return 7;
641 if (uValue < 100000000)
642 return 8;
643 if (uValue < 1000000000)
644 return 9;
645 return 10;
646}
647
648
649/**
650 * Formats the given group ID according to the specified options.
651 *
652 * @returns pszDst
653 * @param pOpts The options and state.
654 * @param gid The GID to format.
655 * @param pszOwner The owner returned by the FS.
656 * @param pszDst The output buffer.
657 * @param cbDst The output buffer size.
658 */
659static const char *rtCmdLsDecimalFormatGroup(PRTCMDLSOPTS pOpts, RTGID gid, const char *pszGroup, char *pszDst, size_t cbDst)
660{
661 if (!pOpts->fNumericalIds)
662 {
663 if (pszGroup)
664 {
665 RTStrCopy(pszDst, cbDst, pszGroup);
666 return pszDst;
667 }
668 if (gid == NIL_RTGID)
669 return "<Nil>";
670 }
671 RTStrFormatU64(pszDst, cbDst, gid, 10, 0, 0, 0);
672 return pszDst;
673}
674
675
676/**
677 * Formats the given user ID according to the specified options.
678 *
679 * @returns pszDst
680 * @param pOpts The options and state.
681 * @param uid The UID to format.
682 * @param pszOwner The owner returned by the FS.
683 * @param pszDst The output buffer.
684 * @param cbDst The output buffer size.
685 */
686static const char *rtCmdLsDecimalFormatOwner(PRTCMDLSOPTS pOpts, RTUID uid, const char *pszOwner, char *pszDst, size_t cbDst)
687{
688 if (!pOpts->fNumericalIds)
689 {
690 if (pszOwner)
691 {
692 RTStrCopy(pszDst, cbDst, pszOwner);
693 return pszDst;
694 }
695 if (uid == NIL_RTUID)
696 return "<Nil>";
697 }
698 RTStrFormatU64(pszDst, cbDst, uid, 10, 0, 0, 0);
699 return pszDst;
700}
701
702
703/**
704 * Formats the given timestamp according to the desired --time-style.
705 *
706 * @returns pszDst
707 * @param pOpts The options and state.
708 * @param pTimestamp The timestamp.
709 * @param pszDst The output buffer.
710 * @param cbDst The output buffer size.
711 */
712static const char *rtCmdLsFormatTimestamp(PRTCMDLSOPTS pOpts, PCRTTIMESPEC pTimestamp, char *pszDst, size_t cbDst)
713{
714 /** @todo timestamp formatting according to the given style. */
715 RT_NOREF(pOpts);
716 return RTTimeSpecToString(pTimestamp, pszDst, cbDst);
717}
718
719
720
721/**
722 * RTCMDLSFORMAT_MACHINE_READABLE: --machine-readable
723 */
724static RTEXITCODE rtCmdLsDisplayCollectionInMachineReadableFormat(PRTCMDLSOPTS pOpts, PRTCMDLSCOLLECTION pCollection,
725 char *pszTmp, size_t cbTmp)
726{
727 RT_NOREF(pOpts, pCollection, pszTmp, cbTmp);
728 RTMsgError("Machine readable format not implemented\n");
729 return RTEXITCODE_FAILURE;
730}
731
732
733/**
734 * RTCMDLSFORMAT_COMMAS: -m
735 */
736static RTEXITCODE rtCmdLsDisplayCollectionInCvsFormat(PRTCMDLSOPTS pOpts, PRTCMDLSCOLLECTION pCollection,
737 char *pszTmp, size_t cbTmp)
738{
739 RT_NOREF(pOpts, pCollection, pszTmp, cbTmp);
740 RTMsgError("Table output formats not implemented\n");
741 return RTEXITCODE_FAILURE;
742}
743
744
745/**
746 * RTCMDLSFORMAT_LONG: -l
747 */
748static RTEXITCODE rtCmdLsDisplayCollectionInLongFormat(PRTCMDLSOPTS pOpts, PRTCMDLSCOLLECTION pCollection,
749 char *pszTmp, size_t cbTmp, size_t cchAllocatedCol)
750{
751 /*
752 * Figure the width of the size, the link count, the uid, the gid, and the inode columns.
753 */
754 size_t cchSizeCol = 1;
755 size_t cchLinkCol = 1;
756 size_t cchUidCol = pOpts->fShowOwner ? 1 : 0;
757 size_t cchGidCol = pOpts->fShowGroup ? 1 : 0;
758 size_t cchINodeCol = pOpts->fShowINode ? 1 : 0;
759
760 size_t i = pCollection->cEntries;
761 while (i-- > 0)
762 {
763 PRTCMDLSENTRY pEntry = pCollection->papEntries[i];
764
765 rtCmdLsFormatSize(pOpts, pEntry->Info.cbObject, pszTmp, cbTmp);
766 size_t cchTmp = strlen(pszTmp);
767 if (cchTmp > cchSizeCol)
768 cchSizeCol = cchTmp;
769
770 cchTmp = rtCmdLsDecimalFormatLengthU32(pEntry->Info.Attr.u.Unix.cHardlinks) + 1;
771 if (cchTmp > cchLinkCol)
772 cchLinkCol = cchTmp;
773
774 if (pOpts->fShowOwner)
775 {
776 rtCmdLsDecimalFormatOwner(pOpts, pEntry->Info.Attr.u.Unix.uid, pEntry->pszOwner, pszTmp, cbTmp);
777 cchTmp = strlen(pszTmp);
778 if (cchTmp > cchUidCol)
779 cchUidCol = cchTmp;
780 }
781
782 if (pOpts->fShowGroup)
783 {
784 rtCmdLsDecimalFormatGroup(pOpts, pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp);
785 cchTmp = strlen(pszTmp);
786 if (cchTmp > cchGidCol)
787 cchGidCol = cchTmp;
788 }
789
790 if (pOpts->fShowINode)
791 {
792 cchTmp = RTStrFormatU64(pszTmp, cchTmp, pEntry->Info.Attr.u.Unix.INodeId, 10, 0, 0, 0);
793 if (cchTmp > cchINodeCol)
794 cchINodeCol = cchTmp;
795 }
796 }
797
798 /*
799 * Determin time member offset.
800 */
801 size_t offTime;
802 switch (pOpts->enmTime)
803 {
804 default: AssertFailed(); RT_FALL_THRU();
805 case RTCMDLSTIME_MTIME: offTime = RT_OFFSETOF(RTCMDLSENTRY, Info.ModificationTime); break;
806 case RTCMDLSTIME_BTIME: offTime = RT_OFFSETOF(RTCMDLSENTRY, Info.BirthTime); break;
807 case RTCMDLSTIME_CTIME: offTime = RT_OFFSETOF(RTCMDLSENTRY, Info.ChangeTime); break;
808 case RTCMDLSTIME_ATIME: offTime = RT_OFFSETOF(RTCMDLSENTRY, Info.AccessTime); break;
809 }
810
811 /*
812 * Display the entries.
813 */
814 for (i = 0; i < pCollection->cEntries; i++)
815 {
816 PRTCMDLSENTRY pEntry = pCollection->papEntries[i];
817
818 if (cchINodeCol)
819 RTPrintf("%*RU64 ", cchINodeCol, pEntry->Info.Attr.u.Unix.INodeId);
820 if (cchAllocatedCol)
821 RTPrintf("%*s ", cchAllocatedCol, rtCmdLsFormatBlocks(pOpts, pEntry->Info.cbAllocated, pszTmp, cbTmp));
822
823 RTFMODE fMode = pEntry->Info.Attr.fMode;
824 switch (fMode & RTFS_TYPE_MASK)
825 {
826 case RTFS_TYPE_FIFO: RTPrintf("f"); break;
827 case RTFS_TYPE_DEV_CHAR: RTPrintf("c"); break;
828 case RTFS_TYPE_DIRECTORY: RTPrintf("d"); break;
829 case RTFS_TYPE_DEV_BLOCK: RTPrintf("b"); break;
830 case RTFS_TYPE_FILE: RTPrintf("-"); break;
831 case RTFS_TYPE_SYMLINK: RTPrintf("l"); break;
832 case RTFS_TYPE_SOCKET: RTPrintf("s"); break;
833 case RTFS_TYPE_WHITEOUT: RTPrintf("w"); break;
834 default: RTPrintf("?"); AssertFailed(); break;
835 }
836 /** @todo sticy bits++ */
837 RTPrintf("%c%c%c",
838 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
839 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
840 fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
841 RTPrintf("%c%c%c",
842 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
843 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
844 fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
845 RTPrintf("%c%c%c",
846 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
847 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
848 fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
849 if (1)
850 {
851 RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
852 fMode & RTFS_DOS_READONLY ? 'R' : '-',
853 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
854 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
855 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
856 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
857 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
858 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
859 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
860 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
861 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
862 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
863 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
864 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
865 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
866 }
867 RTPrintf(" %*u", cchLinkCol, pEntry->Info.Attr.u.Unix.cHardlinks);
868 if (cchUidCol)
869 RTPrintf(" %*s", cchUidCol,
870 rtCmdLsDecimalFormatOwner(pOpts, pEntry->Info.Attr.u.Unix.uid, pEntry->pszOwner, pszTmp, cbTmp));
871 if (cchGidCol)
872 RTPrintf(" %*s", cchGidCol,
873 rtCmdLsDecimalFormatGroup(pOpts, pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp));
874 RTPrintf(" %*s", cchSizeCol, rtCmdLsFormatSize(pOpts, pEntry->Info.cbObject, pszTmp, cbTmp));
875
876 PCRTTIMESPEC pTime = (PCRTTIMESPEC)((uintptr_t)pEntry + offTime);
877 RTPrintf(" %s", rtCmdLsFormatTimestamp(pOpts, pTime, pszTmp, cbTmp));
878
879 RTPrintf(" %s\n", rtCmdLsFormatName(pOpts, pEntry->szName, pszTmp, cbTmp));
880 }
881
882 return RTEXITCODE_SUCCESS;
883}
884
885
886/**
887 * RTCMDLSFORMAT_SINGLE: -1
888 */
889static RTEXITCODE rtCmdLsDisplayCollectionInSingleFormat(PRTCMDLSOPTS pOpts, PRTCMDLSCOLLECTION pCollection,
890 char *pszTmp, size_t cbTmp, size_t cchAllocatedCol)
891{
892 if (cchAllocatedCol > 0)
893 for (size_t i = 0; i < pCollection->cEntries; i++)
894 {
895 PRTCMDLSENTRY pEntry = pCollection->papEntries[i];
896 RTPrintf("%*s %s\n",
897 cchAllocatedCol, rtCmdLsFormatBlocks(pOpts, pEntry->Info.cbAllocated, pszTmp, cbTmp / 4),
898 rtCmdLsFormatName(pOpts, pEntry->szName, &pszTmp[cbTmp / 4], cbTmp / 4 * 3));
899 }
900 else
901 for (size_t i = 0; i < pCollection->cEntries; i++)
902 {
903 PRTCMDLSENTRY pEntry = pCollection->papEntries[i];
904 RTPrintf("%s\n", rtCmdLsFormatName(pOpts, pEntry->szName, pszTmp, cbTmp));
905 }
906
907 return RTEXITCODE_SUCCESS;
908}
909
910
911/**
912 * RTCMDLSFORMAT_COLS_VERTICAL: default, -C; RTCMDLSFORMAT_COLS_HORIZONTAL: -x
913 */
914static RTEXITCODE rtCmdLsDisplayCollectionInTableFormat(PRTCMDLSOPTS pOpts, PRTCMDLSCOLLECTION pCollection,
915 char *pszTmp, size_t cbTmp, size_t cchAllocatedCol)
916{
917 RT_NOREF(pOpts, pCollection, pszTmp, cbTmp, cchAllocatedCol);
918 RTMsgError("Table output formats not implemented\n");
919 return RTEXITCODE_FAILURE;
920}
921
922
923/**
924 * Does the actual displaying of the entry collections.
925 *
926 * @returns Program exit code.
927 * @param pOpts The options and state.
928 */
929static RTEXITCODE rtCmdLsDisplayCollections(PRTCMDLSOPTS pOpts)
930{
931 rtCmdLsSortCollections(pOpts);
932
933 bool const fNeedCollectionName = pOpts->cCollections > 2
934 || ( pOpts->cCollections == 2
935 && pOpts->papCollections[0]->cEntries > 0);
936 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
937 for (size_t iCollection = 0; iCollection < pOpts->cCollections; iCollection++)
938 {
939 PRTCMDLSCOLLECTION pCollection = pOpts->papCollections[iCollection];
940 char szTmp[RTPATH_MAX*2];
941
942 /* The header. */
943 if (iCollection != 0)
944 {
945 if ( iCollection > 1
946 || pOpts->papCollections[0]->cEntries > 0)
947 RTPrintf("\n");
948 if (fNeedCollectionName)
949 RTPrintf("%s:\n", rtCmdLsFormatName(pOpts, pCollection->szName, szTmp, sizeof(szTmp)));
950 RTPrintf("total %s\n", rtCmdLsFormatBlocks(pOpts, pCollection->cbTotalAllocated, szTmp, sizeof(szTmp)));
951 }
952
953 /* Format the entries. */
954 RTEXITCODE rcExit2;
955 if (pOpts->enmFormat == RTCMDLSFORMAT_MACHINE_READABLE)
956 rcExit2 = rtCmdLsDisplayCollectionInMachineReadableFormat(pOpts, pCollection, szTmp, sizeof(szTmp));
957 else if (pOpts->enmFormat == RTCMDLSFORMAT_COMMAS)
958 rcExit2 = rtCmdLsDisplayCollectionInCvsFormat(pOpts, pCollection, szTmp, sizeof(szTmp));
959 else
960 {
961 /* If the allocated size is requested, calculate the column width. */
962 size_t cchAllocatedCol = 0;
963 if (pOpts->fShowAllocatedSize)
964 {
965 size_t i = pCollection->cEntries;
966 while (i-- > 0)
967 {
968 rtCmdLsFormatBlocks(pOpts, pCollection->papEntries[i]->Info.cbAllocated, szTmp, sizeof(szTmp));
969 size_t cchTmp = strlen(szTmp);
970 if (cchTmp > cchAllocatedCol)
971 cchAllocatedCol = cchTmp;
972 }
973 }
974
975 /* Do the individual formatting. */
976 if (pOpts->enmFormat == RTCMDLSFORMAT_LONG)
977 rcExit2 = rtCmdLsDisplayCollectionInLongFormat(pOpts, pCollection, szTmp, sizeof(szTmp), cchAllocatedCol);
978 else if (pOpts->enmFormat == RTCMDLSFORMAT_SINGLE)
979 rcExit2 = rtCmdLsDisplayCollectionInSingleFormat(pOpts, pCollection, szTmp, sizeof(szTmp), cchAllocatedCol);
980 else
981 rcExit2 = rtCmdLsDisplayCollectionInTableFormat(pOpts, pCollection, szTmp, sizeof(szTmp), cchAllocatedCol);
982 }
983 if (rcExit2 != RTEXITCODE_SUCCESS)
984 rcExit = rcExit2;
985 }
986 return rcExit;
987}
988
989
990/**
991 * Frees all collections and their entries.
992 * @param pOpts The options and state.
993 */
994static void rtCmdLsFreeCollections(PRTCMDLSOPTS pOpts)
995{
996 size_t i = pOpts->cCollections;
997 while (i-- > 0)
998 {
999 PRTCMDLSCOLLECTION pCollection = pOpts->papCollections[i];
1000 PPRTCMDLSENTRY papEntries = pCollection->papEntries;
1001 size_t j = pCollection->cEntries;
1002 while (j-- > 0)
1003 {
1004 RTMemFree(papEntries[j]);
1005 papEntries[j] = NULL;
1006 }
1007 RTMemFree(papEntries);
1008 pCollection->papEntries = NULL;
1009 pCollection->cEntries = 0;
1010 pCollection->cEntriesAllocated = 0;
1011 RTMemFree(pCollection);
1012 pOpts->papCollections[i] = NULL;
1013 }
1014
1015 RTMemFree(pOpts->papCollections);
1016 pOpts->papCollections = NULL;
1017 pOpts->cCollections = 0;
1018 pOpts->cCollectionsAllocated = 0;
1019}
1020
1021
1022/**
1023 * Allocates a new collection.
1024 *
1025 * @returns Pointer to the collection.
1026 * @param pOpts The options and state.
1027 * @param pszName The collection name. Empty for special first
1028 * collection.
1029 */
1030static PRTCMDLSCOLLECTION rtCmdLsNewCollection(PRTCMDLSOPTS pOpts, const char *pszName)
1031{
1032 /* Grow the pointer table? */
1033 if (pOpts->cCollections >= pOpts->cCollectionsAllocated)
1034 {
1035 size_t cNew = pOpts->cCollectionsAllocated ? pOpts->cCollectionsAllocated * 2 : 16;
1036 void *pvNew = RTMemRealloc(pOpts->papCollections, cNew * sizeof(pOpts->papCollections[0]));
1037 if (!pvNew)
1038 {
1039 RTMsgError("Out of memory! (resize collections)");
1040 return NULL;
1041 }
1042 pOpts->cCollectionsAllocated = cNew;
1043 pOpts->papCollections = (PPRTCMDLSCOLLECTION)pvNew;
1044
1045 /* If this is the first time and pszName isn't empty, add the zero'th
1046 entry for the command line stuff (hardcoded first collection). */
1047 if ( pOpts->cCollections == 0
1048 && *pszName)
1049 {
1050 PRTCMDLSCOLLECTION pCollection = (PRTCMDLSCOLLECTION)RTMemAllocZ(RT_OFFSETOF(RTCMDLSCOLLECTION, szName[1]));
1051 if (!pCollection)
1052 {
1053 RTMsgError("Out of memory! (collection)");
1054 return NULL;
1055 }
1056 pOpts->papCollections[0] = pCollection;
1057 pOpts->cCollections = 1;
1058 }
1059 }
1060
1061 /* Add new collection. */
1062 size_t cbName = strlen(pszName) + 1;
1063 PRTCMDLSCOLLECTION pCollection = (PRTCMDLSCOLLECTION)RTMemAllocZ(RT_OFFSETOF(RTCMDLSCOLLECTION, szName[cbName]));
1064 if (pCollection)
1065 {
1066 memcpy(pCollection->szName, pszName, cbName);
1067 pOpts->papCollections[pOpts->cCollections++] = pCollection;
1068 }
1069 else
1070 RTMsgError("Out of memory! (collection)");
1071 return pCollection;
1072}
1073
1074
1075/**
1076 * Adds one entry to a collection.
1077 * @returns Program exit code
1078 * @param pCollection The collection.
1079 * @param pszEntry The entry name.
1080 * @param pInfo The entry info.
1081 * @param pszOwner The owner name if available, otherwise NULL.
1082 * @param pszGroup The group anme if available, otherwise NULL.
1083 * @param pszTarget The symbolic link target if applicable and
1084 * available, otherwise NULL.
1085 */
1086static RTEXITCODE rtCmdLsAddOne(PRTCMDLSCOLLECTION pCollection, const char *pszEntry, PRTFSOBJINFO pInfo,
1087 const char *pszOwner, const char *pszGroup, const char *pszTarget)
1088{
1089
1090 /* Make sure there is space in the collection for the new entry. */
1091 if (pCollection->cEntries >= pCollection->cEntriesAllocated)
1092 {
1093 size_t cNew = pCollection->cEntriesAllocated ? pCollection->cEntriesAllocated * 2 : 16;
1094 void *pvNew = RTMemRealloc(pCollection->papEntries, cNew * sizeof(pCollection->papEntries[0]));
1095 if (!pvNew)
1096 return RTMsgErrorExitFailure("Out of memory! (resize entries)");
1097 pCollection->papEntries = (PPRTCMDLSENTRY)pvNew;
1098 pCollection->cEntriesAllocated = cNew;
1099 }
1100
1101 /* Create and insert a new entry. */
1102 size_t const cchEntry = strlen(pszEntry);
1103 size_t const cbOwner = pszOwner ? strlen(pszOwner) + 1 : 0;
1104 size_t const cbGroup = pszGroup ? strlen(pszGroup) + 1 : 0;
1105 size_t const cbTarget = pszTarget ? strlen(pszTarget) + 1 : 0;
1106 size_t const cbEntry = RT_OFFSETOF(RTCMDLSENTRY, szName[cchEntry + 1 + cbOwner + cbGroup + cbTarget]);
1107 PRTCMDLSENTRY pEntry = (PRTCMDLSENTRY)RTMemAlloc(cbEntry);
1108 if (pEntry)
1109 {
1110 pEntry->Info = *pInfo;
1111 pEntry->pszTarget = NULL; /** @todo symbolic links. */
1112 pEntry->pszOwner = NULL;
1113 pEntry->pszGroup = NULL;
1114 pEntry->cchName = cchEntry;
1115 memcpy(pEntry->szName, pszEntry, cchEntry);
1116 pEntry->szName[cchEntry] = '\0';
1117
1118 char *psz = &pEntry->szName[cchEntry + 1];
1119 if (pszTarget)
1120 {
1121 pEntry->pszTarget = psz;
1122 memcpy(psz, pszTarget, cbTarget);
1123 psz += cbTarget;
1124 }
1125 if (pszOwner)
1126 {
1127 pEntry->pszOwner = psz;
1128 memcpy(psz, pszOwner, cbOwner);
1129 psz += cbOwner;
1130 }
1131 if (pszGroup)
1132 {
1133 pEntry->pszGroup = psz;
1134 memcpy(psz, pszGroup, cbGroup);
1135 }
1136
1137 pCollection->papEntries[pCollection->cEntries++] = pEntry;
1138 pCollection->cbTotalAllocated += pEntry->Info.cbAllocated;
1139 pCollection->cbTotalFiles += pEntry->Info.cbObject;
1140 return RTEXITCODE_SUCCESS;
1141 }
1142 return RTMsgErrorExitFailure("Out of memory! (entry)");
1143}
1144
1145
1146/**
1147 * Checks if the entry is to be filtered out.
1148 *
1149 * @returns true if filtered out, false if included.
1150 * @param pOpts The options and state.
1151 * @param pszEntry The entry name.
1152 * @param pInfo The entry info.
1153 */
1154static bool rtCmdLsIsFilteredOut(PRTCMDLSOPTS pOpts, const char *pszEntry, PCRTFSOBJINFO pInfo)
1155{
1156 /*
1157 * Should we filter out this entry?
1158 */
1159 if ( !pOpts->fShowHidden
1160 && (pInfo->Attr.fMode & RTFS_DOS_HIDDEN))
1161 return true;
1162
1163 size_t const cchEntry = strlen(pszEntry);
1164 if ( !pOpts->fShowDotAndDotDot
1165 && cchEntry <= 2
1166 && pszEntry[0] == '.'
1167 && ( cchEntry == 1
1168 || pszEntry[1] == '.' ))
1169 return true;
1170
1171 if ( !pOpts->fShowBackups
1172 && pszEntry[cchEntry - 1] == '~')
1173 return true;
1174 return false;
1175}
1176
1177
1178/**
1179 * Processes a directory, recursing into subdirectories if desired.
1180 *
1181 * @returns Program exit code.
1182 * @param pOpts The options.
1183 * @param hVfsDir The directory.
1184 * @param pszPath Path buffer, RTPATH_MAX in size.
1185 * @param cchPath The length of the current path.
1186 * @param pInfo The parent information.
1187 */
1188static RTEXITCODE rtCmdLsProcessDirectory(PRTCMDLSOPTS pOpts, RTVFSDIR hVfsDir, char *pszPath, size_t cchPath, PCRTFSOBJINFO pInfo)
1189{
1190 /*
1191 * Create a new collection for this directory.
1192 */
1193 RT_NOREF(pInfo);
1194 PRTCMDLSCOLLECTION pCollection = rtCmdLsNewCollection(pOpts, pszPath);
1195 if (!pCollection)
1196 return RTEXITCODE_FAILURE;
1197
1198 /*
1199 * Process the directory entries.
1200 */
1201 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1202 size_t cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
1203 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1204 if (!pDirEntry)
1205 return RTMsgErrorExitFailure("Out of memory! (direntry buffer)");
1206
1207 for (;;)
1208 {
1209 /*
1210 * Read the next entry.
1211 */
1212 size_t cbDirEntry = cbDirEntryAlloced;
1213 int rc = RTVfsDirReadEx(hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1214 if (RT_FAILURE(rc))
1215 {
1216 if (rc == VERR_BUFFER_OVERFLOW)
1217 {
1218 RTMemTmpFree(pDirEntry);
1219 cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
1220 pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1221 if (pDirEntry)
1222 continue;
1223 rcExit = RTMsgErrorExitFailure("Out of memory (direntry buffer)");
1224 }
1225 else if (rc != VERR_NO_MORE_FILES)
1226 rcExit = RTMsgErrorExitFailure("RTVfsDirReadEx failed: %Rrc\n", rc);
1227 break;
1228 }
1229
1230 /*
1231 * Process the entry.
1232 */
1233 if (rtCmdLsIsFilteredOut(pOpts, pDirEntry->szName, &pDirEntry->Info))
1234 continue;
1235
1236
1237 const char *pszOwner = NULL;
1238 RTFSOBJINFO OwnerInfo;
1239 if (pDirEntry->Info.Attr.u.Unix.uid != NIL_RTUID && pOpts->fShowOwner)
1240 {
1241 rc = RTVfsDirQueryPathInfo(hVfsDir, pDirEntry->szName, &OwnerInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
1242 if (RT_SUCCESS(rc) && OwnerInfo.Attr.u.UnixOwner.szName[0])
1243 pszOwner = &OwnerInfo.Attr.u.UnixOwner.szName[0];
1244 }
1245
1246 const char *pszGroup = NULL;
1247 RTFSOBJINFO GroupInfo;
1248 if (pDirEntry->Info.Attr.u.Unix.gid != NIL_RTGID && pOpts->fShowGroup)
1249 {
1250 rc = RTVfsDirQueryPathInfo(hVfsDir, pDirEntry->szName, &GroupInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
1251 if (RT_SUCCESS(rc) && GroupInfo.Attr.u.UnixGroup.szName[0])
1252 pszGroup = &GroupInfo.Attr.u.UnixGroup.szName[0];
1253 }
1254
1255 RTEXITCODE rcExit2 = rtCmdLsAddOne(pCollection, pDirEntry->szName, &pDirEntry->Info, pszOwner, pszGroup, NULL);
1256 if (rcExit2 != RTEXITCODE_SUCCESS)
1257 rcExit = rcExit2;
1258 }
1259
1260 RTMemTmpFree(pDirEntry);
1261
1262 /*
1263 * Recurse into subdirectories if requested.
1264 */
1265 if (pOpts->fRecursive)
1266 {
1267 for (uint32_t i = 0; i < pCollection->cEntries; i++)
1268 {
1269 PRTCMDLSENTRY pEntry = pCollection->papEntries[i];
1270 if (RTFS_IS_SYMLINK(pEntry->Info.Attr.fMode))
1271 {
1272 if (!pOpts->fFollowSymlinksInDirs)
1273 continue;
1274 /** @todo implement following symbolic links in the tree. */
1275 continue;
1276 }
1277 else if ( !RTFS_IS_DIRECTORY(pEntry->Info.Attr.fMode)
1278 || ( pEntry->szName[0] == '.'
1279 && ( pEntry->szName[1] == '\0'
1280 || ( pEntry->szName[1] == '.'
1281 && pEntry->szName[2] == '\0'))) )
1282 continue;
1283
1284 /* Open subdirectory and process it. */
1285 RTVFSDIR hSubDir;
1286 int rc = RTVfsDirOpenDir(hVfsDir, pEntry->szName, 0 /*fFlags*/, &hSubDir);
1287 if (RT_SUCCESS(rc))
1288 {
1289 if (cchPath + 1 + pEntry->cchName + 1 < RTPATH_MAX)
1290 {
1291 pszPath[cchPath] = RTPATH_SLASH;
1292 memcpy(&pszPath[cchPath + 1], pEntry->szName, pEntry->cchName + 1);
1293 RTEXITCODE rcExit2 = rtCmdLsProcessDirectory(pOpts, hSubDir, pszPath,
1294 cchPath + 1 + pEntry->cchName, &pEntry->Info);
1295 if (rcExit2 != RTEXITCODE_SUCCESS)
1296 rcExit = rcExit2;
1297 pszPath[cchPath] = '\0';
1298 }
1299 else
1300 rcExit = RTMsgErrorExitFailure("Too deep recursion: %s%c%s", pszPath, RTPATH_SLASH, pEntry->szName);
1301 }
1302 else
1303 rcExit = RTMsgErrorExitFailure("RTVfsDirOpenDir failed on %s in %s: %Rrc\n", pEntry->szName, pszPath, rc);
1304 }
1305 }
1306 return rcExit;
1307}
1308
1309
1310/**
1311 * Processes one argument.
1312 *
1313 * @returns Program exit code.
1314 * @param pOpts The options.
1315 * @param pszArg The argument.
1316 */
1317static RTEXITCODE rtCmdLsProcessArgument(PRTCMDLSOPTS pOpts, const char *pszArg)
1318{
1319 /*
1320 * Query info about the object 'pszArg' indicates.
1321 */
1322 RTERRINFOSTATIC ErrInfo;
1323 uint32_t offError;
1324 RTFSOBJINFO Info;
1325 uint32_t fPath = pOpts->fFollowSymlinkToAnyArgs ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK;
1326 int rc = RTVfsChainQueryInfo(pszArg, &Info, RTFSOBJATTRADD_UNIX, fPath, &offError, RTErrInfoInitStatic(&ErrInfo));
1327 if (RT_FAILURE(rc))
1328 return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszArg, rc, offError, &ErrInfo.Core);
1329
1330 /* Symbolic links requires special handling of course. */
1331 if (RTFS_IS_SYMLINK(Info.Attr.fMode))
1332 {
1333 if (pOpts->fFollowSymlinkToDirArgs)
1334 {
1335 RTFSOBJINFO Info2;
1336 rc = RTVfsChainQueryInfo(pszArg, &Info2, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK,
1337 &offError, RTErrInfoInitStatic(&ErrInfo));
1338 if (RT_SUCCESS(rc) && !RTFS_IS_DIRECTORY(Info.Attr.fMode))
1339 Info = Info2;
1340 }
1341 }
1342
1343 /*
1344 * If it's not a directory or we've been told to process directories
1345 * without going into them, just add it to the default collection.
1346 */
1347 if ( !pOpts->fFollowDirectoryArgs
1348 || !RTFS_IS_DIRECTORY(Info.Attr.fMode))
1349 {
1350 if ( pOpts->cCollections > 0
1351 || rtCmdLsNewCollection(pOpts, "") != NULL)
1352 {
1353 RTFSOBJINFO OwnerInfo;
1354 if (Info.Attr.u.Unix.uid != NIL_RTUID && pOpts->fShowOwner)
1355 rc = RTVfsChainQueryInfo(pszArg, &OwnerInfo, RTFSOBJATTRADD_UNIX_OWNER, fPath, NULL, NULL);
1356 else
1357 rc = VERR_NOT_SUPPORTED;
1358 const char *pszOwner = RT_SUCCESS(rc) && OwnerInfo.Attr.u.UnixOwner.szName[0]
1359 ? &OwnerInfo.Attr.u.UnixOwner.szName[0] : NULL;
1360
1361 RTFSOBJINFO GroupInfo;
1362 if (Info.Attr.u.Unix.gid != NIL_RTGID && pOpts->fShowGroup)
1363 rc = RTVfsChainQueryInfo(pszArg, &GroupInfo, RTFSOBJATTRADD_UNIX_GROUP, fPath, NULL, NULL);
1364 else
1365 rc = VERR_NOT_SUPPORTED;
1366 const char *pszGroup = RT_SUCCESS(rc) && GroupInfo.Attr.u.UnixGroup.szName[0]
1367 ? &GroupInfo.Attr.u.UnixGroup.szName[0] : NULL;
1368
1369 return rtCmdLsAddOne(pOpts->papCollections[0], pszArg, &Info, pszOwner, pszGroup, NULL);
1370 }
1371 return RTEXITCODE_FAILURE;
1372 }
1373
1374 /*
1375 * Open the directory.
1376 */
1377 RTVFSDIR hVfsDir;
1378 rc = RTVfsChainOpenDir(pszArg, 0 /*fFlags*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo));
1379 if (RT_FAILURE(rc))
1380 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenDir", pszArg, rc, offError, &ErrInfo.Core);
1381
1382 RTEXITCODE rcExit;
1383 char szPath[RTPATH_MAX];
1384 size_t cchPath = strlen(pszArg);
1385 if (cchPath < sizeof(szPath))
1386 {
1387 memcpy(szPath, pszArg, cchPath + 1);
1388 rcExit = rtCmdLsProcessDirectory(pOpts, hVfsDir, szPath, cchPath, &Info);
1389 }
1390 else
1391 rcExit = RTMsgErrorExitFailure("Too long argument: %s", pszArg);
1392 RTVfsDirRelease(hVfsDir);
1393 return rcExit;
1394}
1395
1396
1397/**
1398 * A /bin/cat clone.
1399 *
1400 * @returns Program exit code.
1401 *
1402 * @param cArgs The number of arguments.
1403 * @param papszArgs The argument vector. (Note that this may be
1404 * reordered, so the memory must be writable.)
1405 */
1406RTEXITCODE RTCmdLs(unsigned cArgs, char **papszArgs)
1407{
1408
1409 /*
1410 * Parse the command line.
1411 */
1412#define OPT_AUTHOR 1000
1413#define OPT_BLOCK_SIZE 1001
1414#define OPT_COLOR 1002
1415#define OPT_FILE_TYPE 1003
1416#define OPT_FORMAT 1004
1417#define OPT_FULL_TIME 1005
1418#define OPT_GROUP_DIRECTORIES_FIRST 1006
1419#define OPT_SI 1007
1420#define OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR 1008
1421#define OPT_HIDE 1009
1422#define OPT_INDICATOR_STYLE 1010
1423#define OPT_MACHINE_READABLE 1011
1424#define OPT_SHOW_CONTROL_CHARS 1012
1425#define OPT_QUOTING_STYLE 1013
1426#define OPT_SORT 1014
1427#define OPT_TIME 1015
1428#define OPT_TIME_STYLE 1016
1429 static const RTGETOPTDEF s_aOptions[] =
1430 {
1431 { "--all", 'a', RTGETOPT_REQ_NOTHING },
1432 { "--almost-all", 'A', RTGETOPT_REQ_NOTHING },
1433 //{ "--author", OPT_AUTHOR, RTGETOPT_REQ_NOTHING },
1434 { "--escape", 'b', RTGETOPT_REQ_NOTHING },
1435 { "--block-size", OPT_BLOCK_SIZE, RTGETOPT_REQ_UINT32 },
1436 { "--ctime", 'c', RTGETOPT_REQ_NOTHING },
1437 //{ "--columns", 'C', RTGETOPT_REQ_NOTHING },
1438 //{ "--color", OPT_COLOR, RTGETOPT_OPT_STRING },
1439 { "--directory", 'd', RTGETOPT_REQ_NOTHING },
1440 //{ "--dired", 'D', RTGETOPT_REQ_NOTHING },
1441 { "--dash-f", 'f', RTGETOPT_REQ_NOTHING },
1442 //{ "--classify", 'F', RTGETOPT_REQ_NOTHING },
1443 //{ "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_NOTHING },
1444 { "--format", OPT_FORMAT, RTGETOPT_REQ_STRING },
1445 { "--full-time", OPT_FULL_TIME, RTGETOPT_REQ_NOTHING },
1446 { "--dash-g", 'g', RTGETOPT_REQ_NOTHING },
1447 { "--group-directories-first", OPT_GROUP_DIRECTORIES_FIRST, RTGETOPT_REQ_NOTHING },
1448 { "--no-group", 'G', RTGETOPT_REQ_NOTHING },
1449 { "--human-readable", 'h', RTGETOPT_REQ_NOTHING },
1450 { "--si", OPT_SI, RTGETOPT_REQ_NOTHING },
1451 { "--dereference-command-line", 'H', RTGETOPT_REQ_NOTHING },
1452 { "--dereference-command-line-symlink-to-dir", OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR, RTGETOPT_REQ_NOTHING },
1453 //{ "--hide" OPT_HIDE, RTGETOPT_REQ_STRING },
1454 //{ "--indicator-style" OPT_INDICATOR_STYLE, RTGETOPT_REQ_STRING },
1455 { "--inode", 'i', RTGETOPT_REQ_NOTHING },
1456 { "--block-size-1kib", 'k', RTGETOPT_REQ_NOTHING },
1457 { "--long", 'l', RTGETOPT_REQ_NOTHING },
1458 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1459 { "--format-commas", 'm', RTGETOPT_REQ_NOTHING },
1460 { "--machinereadable", OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1461 { "--machine-readable", OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1462 { "--numeric-uid-gid", 'n', RTGETOPT_REQ_NOTHING },
1463 { "--literal", 'N', RTGETOPT_REQ_NOTHING },
1464 { "--long-without-group-info", 'o', RTGETOPT_REQ_NOTHING },
1465 //{ "--indicator-style", 'p', RTGETOPT_REQ_STRING },
1466 { "--hide-control-chars", 'q', RTGETOPT_REQ_NOTHING },
1467 { "--show-control-chars", OPT_SHOW_CONTROL_CHARS, RTGETOPT_REQ_NOTHING },
1468 //{ "--quote-name", 'Q', RTGETOPT_REQ_NOTHING },
1469 //{ "--quoting-style", OPT_QUOTING_STYLE, RTGETOPT_REQ_STRING },
1470 { "--reverse", 'r', RTGETOPT_REQ_NOTHING },
1471 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1472 { "--size", 's', RTGETOPT_REQ_NOTHING },
1473 { "--sort-by-size", 'S', RTGETOPT_REQ_NOTHING },
1474 { "--sort", OPT_SORT, RTGETOPT_REQ_STRING },
1475 { "--time", OPT_TIME, RTGETOPT_REQ_STRING },
1476 { "--time-style", OPT_TIME_STYLE, RTGETOPT_REQ_STRING },
1477 { "--sort-by-time", 't', RTGETOPT_REQ_NOTHING },
1478 { "--tabsize", 'T', RTGETOPT_REQ_UINT8 },
1479 { "--atime", 'u', RTGETOPT_REQ_NOTHING },
1480 { "--unsorted", 'U', RTGETOPT_REQ_NOTHING },
1481 { "--version-sort", 'v', RTGETOPT_REQ_NOTHING },
1482 { "--width", 'w', RTGETOPT_REQ_UINT32 },
1483 { "--list-by-line", 'x', RTGETOPT_REQ_NOTHING },
1484 { "--sort-by-extension", 'X', RTGETOPT_REQ_NOTHING },
1485 { "--one-file-per-line", '1', RTGETOPT_REQ_NOTHING },
1486 { "--help", '?', RTGETOPT_REQ_NOTHING },
1487 };
1488
1489 RTCMDLSOPTS Opts;
1490 Opts.fFollowSymlinksInDirs = false;
1491 Opts.fFollowSymlinkToAnyArgs = false;
1492 Opts.fFollowSymlinkToDirArgs = false;
1493 Opts.fFollowDirectoryArgs = true;
1494 Opts.fRecursive = false;
1495 Opts.fShowHidden = false;
1496 Opts.fShowDotAndDotDot = false;
1497 Opts.fShowBackups = true;
1498 Opts.enmSort = RTCMDLSSORT_NAME;
1499 Opts.fReverseSort = false;
1500 Opts.fGroupDirectoriesFirst = false;
1501 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1502 Opts.fEscapeNonGraphicChars = false;
1503 Opts.fEscapeControlChars = true;
1504 Opts.fHideControlChars = false;
1505 Opts.fHumanReadableSizes = false; /**< -h */
1506 Opts.fSiUnits = false;
1507 Opts.cbBlock = 0;
1508 Opts.fShowOwner = true;
1509 Opts.fShowGroup = true;
1510 Opts.fNumericalIds = false;
1511 Opts.fShowINode = false;
1512 Opts.fShowAllocatedSize = false;
1513 Opts.cchTab = 8;
1514 Opts.cchWidth = 80;
1515 Opts.enmColor = RTCMDLSCOLOR_NONE;
1516 Opts.enmTime = RTCMDLSTIME_MTIME;
1517 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LOCALE;
1518 Opts.pszTimeCustom = NULL;
1519
1520 Opts.cCollections = 0;
1521 Opts.cCollectionsAllocated = 0;
1522 Opts.papCollections = NULL;
1523
1524
1525 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1526 unsigned cProcessed = 0;
1527 RTVFSIOSTREAM hVfsOutput = NIL_RTVFSIOSTREAM;
1528 int rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1529 true /*fLeaveOpen*/, &hVfsOutput);
1530 if (RT_FAILURE(rc))
1531 return RTMsgErrorExitFailure("RTVfsIoStrmFromStdHandle: %Rrc", rc);
1532
1533 RTGETOPTSTATE GetState;
1534 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1535 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1536 if (RT_FAILURE(rc))
1537 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1538
1539 for (;;)
1540 {
1541 RTGETOPTUNION ValueUnion;
1542 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1543 switch (chOpt)
1544 {
1545 case 0:
1546 /* When reaching the end of arguments without having processed any
1547 files/dirs/whatever yet, we do the current directory. */
1548 if (cProcessed > 0)
1549 {
1550 RTEXITCODE rcExit2 = rtCmdLsDisplayCollections(&Opts);
1551 if (rcExit2 != RTEXITCODE_SUCCESS)
1552 rcExit = rcExit2;
1553 rtCmdLsFreeCollections(&Opts);
1554 return rcExit;
1555 }
1556 ValueUnion.psz = ".";
1557 RT_FALL_THRU();
1558 case VINF_GETOPT_NOT_OPTION:
1559 {
1560 RTEXITCODE rcExit2 = rtCmdLsProcessArgument(&Opts, ValueUnion.psz);
1561 if (rcExit2 != RTEXITCODE_SUCCESS)
1562 rcExit = rcExit2;
1563 cProcessed++;
1564 break;
1565 }
1566
1567 case 'a':
1568 Opts.fShowHidden = true;
1569 Opts.fShowDotAndDotDot = true;
1570 break;
1571
1572 case 'A':
1573 Opts.fShowHidden = true;
1574 Opts.fShowDotAndDotDot = false;
1575 break;
1576
1577 case 'b':
1578 Opts.fEscapeNonGraphicChars = true;
1579 break;
1580
1581 case OPT_BLOCK_SIZE:
1582 if (!ValueUnion.u32)
1583 {
1584 Assert(!Opts.papCollections);
1585 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid block size: %u", ValueUnion.u32);
1586 }
1587 Opts.cbBlock = ValueUnion.u32;
1588 Opts.fHumanReadableSizes = false;
1589 Opts.fSiUnits = false;
1590 break;
1591
1592 case 'c':
1593 Opts.enmTime = RTCMDLSTIME_CTIME;
1594 break;
1595
1596 case 'C':
1597 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1598 break;
1599
1600 case 'd':
1601 Opts.fFollowDirectoryArgs = false;
1602 Opts.fFollowSymlinkToAnyArgs = false;
1603 Opts.fFollowSymlinkToDirArgs = false;
1604 Opts.fRecursive = false;
1605 break;
1606
1607 case 'f':
1608 Opts.fShowHidden = true;
1609 Opts.fShowDotAndDotDot = true;
1610 if (Opts.enmFormat == RTCMDLSFORMAT_LONG)
1611 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1612 Opts.enmColor = RTCMDLSCOLOR_NONE;
1613 Opts.enmSort = RTCMDLSSORT_NONE;
1614 break;
1615
1616 case OPT_FORMAT:
1617 if ( strcmp(ValueUnion.psz, "across") == 0
1618 || strcmp(ValueUnion.psz, "horizontal") == 0)
1619 Opts.enmFormat = RTCMDLSFORMAT_COLS_HORIZONTAL;
1620 else if (strcmp(ValueUnion.psz, "commas") == 0)
1621 Opts.enmFormat = RTCMDLSFORMAT_COMMAS;
1622 else if ( strcmp(ValueUnion.psz, "long") == 0
1623 || strcmp(ValueUnion.psz, "verbose") == 0)
1624 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1625 else if (strcmp(ValueUnion.psz, "single-column") == 0)
1626 Opts.enmFormat = RTCMDLSFORMAT_SINGLE;
1627 else if (strcmp(ValueUnion.psz, "vertical") == 0)
1628 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1629 else if (strcmp(ValueUnion.psz, "machine-readable") == 0)
1630 Opts.enmFormat = RTCMDLSFORMAT_MACHINE_READABLE;
1631 else
1632 {
1633 Assert(!Opts.papCollections);
1634 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown format: %s", ValueUnion.psz);
1635 }
1636 break;
1637
1638 case OPT_FULL_TIME:
1639 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1640 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_FULL_ISO;
1641 break;
1642
1643 case 'g':
1644 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1645 Opts.fShowOwner = false;
1646 break;
1647
1648 case OPT_GROUP_DIRECTORIES_FIRST:
1649 Opts.fGroupDirectoriesFirst = true;
1650 break;
1651
1652 case 'G':
1653 Opts.fShowGroup = false;
1654 break;
1655
1656 case 'h':
1657 Opts.fHumanReadableSizes = true;
1658 Opts.fSiUnits = false;
1659 break;
1660
1661 case OPT_SI:
1662 Opts.fHumanReadableSizes = true;
1663 Opts.fSiUnits = true;
1664 break;
1665
1666 case 'H':
1667 Opts.fFollowSymlinkToAnyArgs = true;
1668 Opts.fFollowSymlinkToDirArgs = true;
1669 break;
1670
1671 case OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR:
1672 Opts.fFollowSymlinkToAnyArgs = false;
1673 Opts.fFollowSymlinkToDirArgs = true;
1674 break;
1675
1676 case 'i':
1677 Opts.fShowINode = true;
1678 break;
1679
1680 case 'k':
1681 Opts.cbBlock = _1K;
1682 Opts.fHumanReadableSizes = false;
1683 Opts.fSiUnits = false;
1684 break;
1685
1686 case 'l':
1687 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1688 break;
1689
1690 case 'L':
1691 Opts.fFollowSymlinksInDirs = true;
1692 Opts.fFollowSymlinkToAnyArgs = true;
1693 Opts.fFollowSymlinkToDirArgs = true;
1694 break;
1695
1696 case 'm':
1697 Opts.enmFormat = RTCMDLSFORMAT_COMMAS;
1698 break;
1699
1700 case OPT_MACHINE_READABLE:
1701 Opts.enmFormat = RTCMDLSFORMAT_MACHINE_READABLE;
1702 break;
1703
1704 case 'n':
1705 Opts.fNumericalIds = true;
1706 break;
1707
1708 case 'N':
1709 Opts.fEscapeNonGraphicChars = false;
1710 Opts.fEscapeControlChars = false;
1711 Opts.fHideControlChars = false;
1712 break;
1713
1714 case 'o':
1715 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1716 Opts.fShowGroup = false;
1717 break;
1718
1719 case 'q':
1720 Opts.fHideControlChars = true;
1721 break;
1722
1723 case OPT_SHOW_CONTROL_CHARS:
1724 Opts.fHideControlChars = true;
1725 break;
1726
1727 case 'r':
1728 Opts.fReverseSort = true;
1729 break;
1730
1731 case 'R':
1732 Opts.fRecursive = true;
1733 break;
1734
1735 case 's':
1736 Opts.fShowAllocatedSize = true;
1737 break;
1738
1739 case 'S':
1740 Opts.enmSort = RTCMDLSSORT_SIZE;
1741 break;
1742
1743 case OPT_SORT:
1744 if (strcmp(ValueUnion.psz, "none") == 0)
1745 Opts.enmSort = RTCMDLSSORT_NONE;
1746 else if (strcmp(ValueUnion.psz, "extension") == 0)
1747 Opts.enmSort = RTCMDLSSORT_EXTENSION;
1748 else if (strcmp(ValueUnion.psz, "size") == 0)
1749 Opts.enmSort = RTCMDLSSORT_SIZE;
1750 else if (strcmp(ValueUnion.psz, "time") == 0)
1751 Opts.enmSort = RTCMDLSSORT_TIME;
1752 else if (strcmp(ValueUnion.psz, "version") == 0)
1753 Opts.enmSort = RTCMDLSSORT_VERSION;
1754 else
1755 {
1756 Assert(!Opts.papCollections);
1757 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown sort by: %s", ValueUnion.psz);
1758 }
1759 break;
1760
1761 case OPT_TIME:
1762 if ( strcmp(ValueUnion.psz, "btime") == 0
1763 || strcmp(ValueUnion.psz, "birth") == 0)
1764 Opts.enmTime = RTCMDLSTIME_BTIME;
1765 else if ( strcmp(ValueUnion.psz, "ctime") == 0
1766 || strcmp(ValueUnion.psz, "status") == 0)
1767 Opts.enmTime = RTCMDLSTIME_CTIME;
1768 else if ( strcmp(ValueUnion.psz, "mtime") == 0
1769 || strcmp(ValueUnion.psz, "write") == 0
1770 || strcmp(ValueUnion.psz, "modify") == 0)
1771 Opts.enmTime = RTCMDLSTIME_MTIME;
1772 else if ( strcmp(ValueUnion.psz, "atime") == 0
1773 || strcmp(ValueUnion.psz, "access") == 0
1774 || strcmp(ValueUnion.psz, "use") == 0)
1775 Opts.enmTime = RTCMDLSTIME_ATIME;
1776 else
1777 {
1778 Assert(!Opts.papCollections);
1779 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown time attribute: %s", ValueUnion.psz);
1780 }
1781 break;
1782
1783 case OPT_TIME_STYLE:
1784 if (strcmp(ValueUnion.psz, "full-iso") == 0)
1785 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_FULL_ISO;
1786 else if (strcmp(ValueUnion.psz, "long-iso") == 0)
1787 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LONG_ISO;
1788 else if (strcmp(ValueUnion.psz, "iso") == 0)
1789 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_ISO;
1790 else if (strcmp(ValueUnion.psz, "locale") == 0)
1791 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LOCALE;
1792 else if (*ValueUnion.psz == '+')
1793 {
1794 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_CUSTOM;
1795 Opts.pszTimeCustom = ValueUnion.psz;
1796 }
1797 else
1798 {
1799 Assert(!Opts.papCollections);
1800 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown sort by: %s", ValueUnion.psz);
1801 }
1802 break;
1803
1804 case 't':
1805 Opts.enmSort = RTCMDLSSORT_TIME;
1806 break;
1807
1808 case 'T':
1809 Opts.cchTab = ValueUnion.u8;
1810 break;
1811
1812 case 'u':
1813 Opts.enmTime = RTCMDLSTIME_ATIME;
1814 break;
1815
1816 case 'U':
1817 Opts.enmSort = RTCMDLSSORT_NONE;
1818 break;
1819
1820 case 'v':
1821 Opts.enmSort = RTCMDLSSORT_VERSION;
1822 break;
1823
1824 case 'w':
1825 Opts.cchWidth = ValueUnion.u32;
1826 break;
1827
1828 case 'x':
1829 Opts.enmFormat = RTCMDLSFORMAT_COLS_HORIZONTAL;
1830 break;
1831
1832 case 'X':
1833 Opts.enmSort = RTCMDLSSORT_EXTENSION;
1834 break;
1835
1836 case '1':
1837 Opts.enmFormat = RTCMDLSFORMAT_SINGLE;
1838 break;
1839
1840 case '?':
1841 RTPrintf("Usage: to be written\nOpts.on dump:\n");
1842 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1843 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1844 Assert(!Opts.papCollections);
1845 return RTEXITCODE_SUCCESS;
1846
1847 case 'V':
1848 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1849 Assert(!Opts.papCollections);
1850 return RTEXITCODE_SUCCESS;
1851
1852 default:
1853 Assert(!Opts.papCollections);
1854 return RTGetOptPrintError(chOpt, &ValueUnion);
1855 }
1856 }
1857}
1858
1859
1860int main(int argc, char **argv)
1861{
1862 int rc = RTR3InitExe(argc, &argv, 0);
1863 if (RT_FAILURE(rc))
1864 return RTMsgInitFailure(rc);
1865 return RTCmdLs(argc, argv);
1866}
1867
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette