VirtualBox

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

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

RTLs: windows build fix

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 71.0 KB
 
1/* $Id: RTLs.cpp 69625 2017-11-09 03:30:49Z 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 const char *pszOwner = NULL;
1354 RTFSOBJINFO OwnerInfo;
1355 if (Info.Attr.u.Unix.uid != NIL_RTUID && pOpts->fShowOwner)
1356 {
1357 rc = RTVfsChainQueryInfo(pszArg, &OwnerInfo, RTFSOBJATTRADD_UNIX_OWNER, fPath, NULL, NULL);
1358 if (RT_SUCCESS(rc) && OwnerInfo.Attr.u.UnixOwner.szName[0])
1359 pszOwner = &OwnerInfo.Attr.u.UnixOwner.szName[0];
1360 }
1361
1362 const char *pszGroup = NULL;
1363 RTFSOBJINFO GroupInfo;
1364 if (Info.Attr.u.Unix.gid != NIL_RTGID && pOpts->fShowGroup)
1365 {
1366 rc = RTVfsChainQueryInfo(pszArg, &GroupInfo, RTFSOBJATTRADD_UNIX_GROUP, fPath, NULL, NULL);
1367 if (RT_SUCCESS(rc) && GroupInfo.Attr.u.UnixGroup.szName[0])
1368 pszGroup = &GroupInfo.Attr.u.UnixGroup.szName[0];
1369 }
1370
1371 return rtCmdLsAddOne(pOpts->papCollections[0], pszArg, &Info, pszOwner, pszGroup, NULL);
1372 }
1373 return RTEXITCODE_FAILURE;
1374 }
1375
1376 /*
1377 * Open the directory.
1378 */
1379 RTVFSDIR hVfsDir;
1380 rc = RTVfsChainOpenDir(pszArg, 0 /*fFlags*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo));
1381 if (RT_FAILURE(rc))
1382 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenDir", pszArg, rc, offError, &ErrInfo.Core);
1383
1384 RTEXITCODE rcExit;
1385 char szPath[RTPATH_MAX];
1386 size_t cchPath = strlen(pszArg);
1387 if (cchPath < sizeof(szPath))
1388 {
1389 memcpy(szPath, pszArg, cchPath + 1);
1390 rcExit = rtCmdLsProcessDirectory(pOpts, hVfsDir, szPath, cchPath, &Info);
1391 }
1392 else
1393 rcExit = RTMsgErrorExitFailure("Too long argument: %s", pszArg);
1394 RTVfsDirRelease(hVfsDir);
1395 return rcExit;
1396}
1397
1398
1399/**
1400 * A /bin/cat clone.
1401 *
1402 * @returns Program exit code.
1403 *
1404 * @param cArgs The number of arguments.
1405 * @param papszArgs The argument vector. (Note that this may be
1406 * reordered, so the memory must be writable.)
1407 */
1408RTEXITCODE RTCmdLs(unsigned cArgs, char **papszArgs)
1409{
1410
1411 /*
1412 * Parse the command line.
1413 */
1414#define OPT_AUTHOR 1000
1415#define OPT_BLOCK_SIZE 1001
1416#define OPT_COLOR 1002
1417#define OPT_FILE_TYPE 1003
1418#define OPT_FORMAT 1004
1419#define OPT_FULL_TIME 1005
1420#define OPT_GROUP_DIRECTORIES_FIRST 1006
1421#define OPT_SI 1007
1422#define OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR 1008
1423#define OPT_HIDE 1009
1424#define OPT_INDICATOR_STYLE 1010
1425#define OPT_MACHINE_READABLE 1011
1426#define OPT_SHOW_CONTROL_CHARS 1012
1427#define OPT_QUOTING_STYLE 1013
1428#define OPT_SORT 1014
1429#define OPT_TIME 1015
1430#define OPT_TIME_STYLE 1016
1431 static const RTGETOPTDEF s_aOptions[] =
1432 {
1433 { "--all", 'a', RTGETOPT_REQ_NOTHING },
1434 { "--almost-all", 'A', RTGETOPT_REQ_NOTHING },
1435 //{ "--author", OPT_AUTHOR, RTGETOPT_REQ_NOTHING },
1436 { "--escape", 'b', RTGETOPT_REQ_NOTHING },
1437 { "--block-size", OPT_BLOCK_SIZE, RTGETOPT_REQ_UINT32 },
1438 { "--ctime", 'c', RTGETOPT_REQ_NOTHING },
1439 //{ "--columns", 'C', RTGETOPT_REQ_NOTHING },
1440 //{ "--color", OPT_COLOR, RTGETOPT_OPT_STRING },
1441 { "--directory", 'd', RTGETOPT_REQ_NOTHING },
1442 //{ "--dired", 'D', RTGETOPT_REQ_NOTHING },
1443 { "--dash-f", 'f', RTGETOPT_REQ_NOTHING },
1444 //{ "--classify", 'F', RTGETOPT_REQ_NOTHING },
1445 //{ "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_NOTHING },
1446 { "--format", OPT_FORMAT, RTGETOPT_REQ_STRING },
1447 { "--full-time", OPT_FULL_TIME, RTGETOPT_REQ_NOTHING },
1448 { "--dash-g", 'g', RTGETOPT_REQ_NOTHING },
1449 { "--group-directories-first", OPT_GROUP_DIRECTORIES_FIRST, RTGETOPT_REQ_NOTHING },
1450 { "--no-group", 'G', RTGETOPT_REQ_NOTHING },
1451 { "--human-readable", 'h', RTGETOPT_REQ_NOTHING },
1452 { "--si", OPT_SI, RTGETOPT_REQ_NOTHING },
1453 { "--dereference-command-line", 'H', RTGETOPT_REQ_NOTHING },
1454 { "--dereference-command-line-symlink-to-dir", OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR, RTGETOPT_REQ_NOTHING },
1455 //{ "--hide" OPT_HIDE, RTGETOPT_REQ_STRING },
1456 //{ "--indicator-style" OPT_INDICATOR_STYLE, RTGETOPT_REQ_STRING },
1457 { "--inode", 'i', RTGETOPT_REQ_NOTHING },
1458 { "--block-size-1kib", 'k', RTGETOPT_REQ_NOTHING },
1459 { "--long", 'l', RTGETOPT_REQ_NOTHING },
1460 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1461 { "--format-commas", 'm', RTGETOPT_REQ_NOTHING },
1462 { "--machinereadable", OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1463 { "--machine-readable", OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1464 { "--numeric-uid-gid", 'n', RTGETOPT_REQ_NOTHING },
1465 { "--literal", 'N', RTGETOPT_REQ_NOTHING },
1466 { "--long-without-group-info", 'o', RTGETOPT_REQ_NOTHING },
1467 //{ "--indicator-style", 'p', RTGETOPT_REQ_STRING },
1468 { "--hide-control-chars", 'q', RTGETOPT_REQ_NOTHING },
1469 { "--show-control-chars", OPT_SHOW_CONTROL_CHARS, RTGETOPT_REQ_NOTHING },
1470 //{ "--quote-name", 'Q', RTGETOPT_REQ_NOTHING },
1471 //{ "--quoting-style", OPT_QUOTING_STYLE, RTGETOPT_REQ_STRING },
1472 { "--reverse", 'r', RTGETOPT_REQ_NOTHING },
1473 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1474 { "--size", 's', RTGETOPT_REQ_NOTHING },
1475 { "--sort-by-size", 'S', RTGETOPT_REQ_NOTHING },
1476 { "--sort", OPT_SORT, RTGETOPT_REQ_STRING },
1477 { "--time", OPT_TIME, RTGETOPT_REQ_STRING },
1478 { "--time-style", OPT_TIME_STYLE, RTGETOPT_REQ_STRING },
1479 { "--sort-by-time", 't', RTGETOPT_REQ_NOTHING },
1480 { "--tabsize", 'T', RTGETOPT_REQ_UINT8 },
1481 { "--atime", 'u', RTGETOPT_REQ_NOTHING },
1482 { "--unsorted", 'U', RTGETOPT_REQ_NOTHING },
1483 { "--version-sort", 'v', RTGETOPT_REQ_NOTHING },
1484 { "--width", 'w', RTGETOPT_REQ_UINT32 },
1485 { "--list-by-line", 'x', RTGETOPT_REQ_NOTHING },
1486 { "--sort-by-extension", 'X', RTGETOPT_REQ_NOTHING },
1487 { "--one-file-per-line", '1', RTGETOPT_REQ_NOTHING },
1488 { "--help", '?', RTGETOPT_REQ_NOTHING },
1489 };
1490
1491 RTCMDLSOPTS Opts;
1492 Opts.fFollowSymlinksInDirs = false;
1493 Opts.fFollowSymlinkToAnyArgs = false;
1494 Opts.fFollowSymlinkToDirArgs = false;
1495 Opts.fFollowDirectoryArgs = true;
1496 Opts.fRecursive = false;
1497 Opts.fShowHidden = false;
1498 Opts.fShowDotAndDotDot = false;
1499 Opts.fShowBackups = true;
1500 Opts.enmSort = RTCMDLSSORT_NAME;
1501 Opts.fReverseSort = false;
1502 Opts.fGroupDirectoriesFirst = false;
1503 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1504 Opts.fEscapeNonGraphicChars = false;
1505 Opts.fEscapeControlChars = true;
1506 Opts.fHideControlChars = false;
1507 Opts.fHumanReadableSizes = false; /**< -h */
1508 Opts.fSiUnits = false;
1509 Opts.cbBlock = 0;
1510 Opts.fShowOwner = true;
1511 Opts.fShowGroup = true;
1512 Opts.fNumericalIds = false;
1513 Opts.fShowINode = false;
1514 Opts.fShowAllocatedSize = false;
1515 Opts.cchTab = 8;
1516 Opts.cchWidth = 80;
1517 Opts.enmColor = RTCMDLSCOLOR_NONE;
1518 Opts.enmTime = RTCMDLSTIME_MTIME;
1519 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LOCALE;
1520 Opts.pszTimeCustom = NULL;
1521
1522 Opts.cCollections = 0;
1523 Opts.cCollectionsAllocated = 0;
1524 Opts.papCollections = NULL;
1525
1526
1527 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1528 unsigned cProcessed = 0;
1529 RTVFSIOSTREAM hVfsOutput = NIL_RTVFSIOSTREAM;
1530 int rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1531 true /*fLeaveOpen*/, &hVfsOutput);
1532 if (RT_FAILURE(rc))
1533 return RTMsgErrorExitFailure("RTVfsIoStrmFromStdHandle: %Rrc", rc);
1534
1535 RTGETOPTSTATE GetState;
1536 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1537 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1538 if (RT_FAILURE(rc))
1539 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1540
1541 for (;;)
1542 {
1543 RTGETOPTUNION ValueUnion;
1544 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1545 switch (chOpt)
1546 {
1547 case 0:
1548 /* When reaching the end of arguments without having processed any
1549 files/dirs/whatever yet, we do the current directory. */
1550 if (cProcessed > 0)
1551 {
1552 RTEXITCODE rcExit2 = rtCmdLsDisplayCollections(&Opts);
1553 if (rcExit2 != RTEXITCODE_SUCCESS)
1554 rcExit = rcExit2;
1555 rtCmdLsFreeCollections(&Opts);
1556 return rcExit;
1557 }
1558 ValueUnion.psz = ".";
1559 RT_FALL_THRU();
1560 case VINF_GETOPT_NOT_OPTION:
1561 {
1562 RTEXITCODE rcExit2 = rtCmdLsProcessArgument(&Opts, ValueUnion.psz);
1563 if (rcExit2 != RTEXITCODE_SUCCESS)
1564 rcExit = rcExit2;
1565 cProcessed++;
1566 break;
1567 }
1568
1569 case 'a':
1570 Opts.fShowHidden = true;
1571 Opts.fShowDotAndDotDot = true;
1572 break;
1573
1574 case 'A':
1575 Opts.fShowHidden = true;
1576 Opts.fShowDotAndDotDot = false;
1577 break;
1578
1579 case 'b':
1580 Opts.fEscapeNonGraphicChars = true;
1581 break;
1582
1583 case OPT_BLOCK_SIZE:
1584 if (!ValueUnion.u32)
1585 {
1586 Assert(!Opts.papCollections);
1587 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid block size: %u", ValueUnion.u32);
1588 }
1589 Opts.cbBlock = ValueUnion.u32;
1590 Opts.fHumanReadableSizes = false;
1591 Opts.fSiUnits = false;
1592 break;
1593
1594 case 'c':
1595 Opts.enmTime = RTCMDLSTIME_CTIME;
1596 break;
1597
1598 case 'C':
1599 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1600 break;
1601
1602 case 'd':
1603 Opts.fFollowDirectoryArgs = false;
1604 Opts.fFollowSymlinkToAnyArgs = false;
1605 Opts.fFollowSymlinkToDirArgs = false;
1606 Opts.fRecursive = false;
1607 break;
1608
1609 case 'f':
1610 Opts.fShowHidden = true;
1611 Opts.fShowDotAndDotDot = true;
1612 if (Opts.enmFormat == RTCMDLSFORMAT_LONG)
1613 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1614 Opts.enmColor = RTCMDLSCOLOR_NONE;
1615 Opts.enmSort = RTCMDLSSORT_NONE;
1616 break;
1617
1618 case OPT_FORMAT:
1619 if ( strcmp(ValueUnion.psz, "across") == 0
1620 || strcmp(ValueUnion.psz, "horizontal") == 0)
1621 Opts.enmFormat = RTCMDLSFORMAT_COLS_HORIZONTAL;
1622 else if (strcmp(ValueUnion.psz, "commas") == 0)
1623 Opts.enmFormat = RTCMDLSFORMAT_COMMAS;
1624 else if ( strcmp(ValueUnion.psz, "long") == 0
1625 || strcmp(ValueUnion.psz, "verbose") == 0)
1626 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1627 else if (strcmp(ValueUnion.psz, "single-column") == 0)
1628 Opts.enmFormat = RTCMDLSFORMAT_SINGLE;
1629 else if (strcmp(ValueUnion.psz, "vertical") == 0)
1630 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1631 else if (strcmp(ValueUnion.psz, "machine-readable") == 0)
1632 Opts.enmFormat = RTCMDLSFORMAT_MACHINE_READABLE;
1633 else
1634 {
1635 Assert(!Opts.papCollections);
1636 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown format: %s", ValueUnion.psz);
1637 }
1638 break;
1639
1640 case OPT_FULL_TIME:
1641 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1642 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_FULL_ISO;
1643 break;
1644
1645 case 'g':
1646 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1647 Opts.fShowOwner = false;
1648 break;
1649
1650 case OPT_GROUP_DIRECTORIES_FIRST:
1651 Opts.fGroupDirectoriesFirst = true;
1652 break;
1653
1654 case 'G':
1655 Opts.fShowGroup = false;
1656 break;
1657
1658 case 'h':
1659 Opts.fHumanReadableSizes = true;
1660 Opts.fSiUnits = false;
1661 break;
1662
1663 case OPT_SI:
1664 Opts.fHumanReadableSizes = true;
1665 Opts.fSiUnits = true;
1666 break;
1667
1668 case 'H':
1669 Opts.fFollowSymlinkToAnyArgs = true;
1670 Opts.fFollowSymlinkToDirArgs = true;
1671 break;
1672
1673 case OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR:
1674 Opts.fFollowSymlinkToAnyArgs = false;
1675 Opts.fFollowSymlinkToDirArgs = true;
1676 break;
1677
1678 case 'i':
1679 Opts.fShowINode = true;
1680 break;
1681
1682 case 'k':
1683 Opts.cbBlock = _1K;
1684 Opts.fHumanReadableSizes = false;
1685 Opts.fSiUnits = false;
1686 break;
1687
1688 case 'l':
1689 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1690 break;
1691
1692 case 'L':
1693 Opts.fFollowSymlinksInDirs = true;
1694 Opts.fFollowSymlinkToAnyArgs = true;
1695 Opts.fFollowSymlinkToDirArgs = true;
1696 break;
1697
1698 case 'm':
1699 Opts.enmFormat = RTCMDLSFORMAT_COMMAS;
1700 break;
1701
1702 case OPT_MACHINE_READABLE:
1703 Opts.enmFormat = RTCMDLSFORMAT_MACHINE_READABLE;
1704 break;
1705
1706 case 'n':
1707 Opts.fNumericalIds = true;
1708 break;
1709
1710 case 'N':
1711 Opts.fEscapeNonGraphicChars = false;
1712 Opts.fEscapeControlChars = false;
1713 Opts.fHideControlChars = false;
1714 break;
1715
1716 case 'o':
1717 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1718 Opts.fShowGroup = false;
1719 break;
1720
1721 case 'q':
1722 Opts.fHideControlChars = true;
1723 break;
1724
1725 case OPT_SHOW_CONTROL_CHARS:
1726 Opts.fHideControlChars = true;
1727 break;
1728
1729 case 'r':
1730 Opts.fReverseSort = true;
1731 break;
1732
1733 case 'R':
1734 Opts.fRecursive = true;
1735 break;
1736
1737 case 's':
1738 Opts.fShowAllocatedSize = true;
1739 break;
1740
1741 case 'S':
1742 Opts.enmSort = RTCMDLSSORT_SIZE;
1743 break;
1744
1745 case OPT_SORT:
1746 if (strcmp(ValueUnion.psz, "none") == 0)
1747 Opts.enmSort = RTCMDLSSORT_NONE;
1748 else if (strcmp(ValueUnion.psz, "extension") == 0)
1749 Opts.enmSort = RTCMDLSSORT_EXTENSION;
1750 else if (strcmp(ValueUnion.psz, "size") == 0)
1751 Opts.enmSort = RTCMDLSSORT_SIZE;
1752 else if (strcmp(ValueUnion.psz, "time") == 0)
1753 Opts.enmSort = RTCMDLSSORT_TIME;
1754 else if (strcmp(ValueUnion.psz, "version") == 0)
1755 Opts.enmSort = RTCMDLSSORT_VERSION;
1756 else
1757 {
1758 Assert(!Opts.papCollections);
1759 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown sort by: %s", ValueUnion.psz);
1760 }
1761 break;
1762
1763 case OPT_TIME:
1764 if ( strcmp(ValueUnion.psz, "btime") == 0
1765 || strcmp(ValueUnion.psz, "birth") == 0)
1766 Opts.enmTime = RTCMDLSTIME_BTIME;
1767 else if ( strcmp(ValueUnion.psz, "ctime") == 0
1768 || strcmp(ValueUnion.psz, "status") == 0)
1769 Opts.enmTime = RTCMDLSTIME_CTIME;
1770 else if ( strcmp(ValueUnion.psz, "mtime") == 0
1771 || strcmp(ValueUnion.psz, "write") == 0
1772 || strcmp(ValueUnion.psz, "modify") == 0)
1773 Opts.enmTime = RTCMDLSTIME_MTIME;
1774 else if ( strcmp(ValueUnion.psz, "atime") == 0
1775 || strcmp(ValueUnion.psz, "access") == 0
1776 || strcmp(ValueUnion.psz, "use") == 0)
1777 Opts.enmTime = RTCMDLSTIME_ATIME;
1778 else
1779 {
1780 Assert(!Opts.papCollections);
1781 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown time attribute: %s", ValueUnion.psz);
1782 }
1783 break;
1784
1785 case OPT_TIME_STYLE:
1786 if (strcmp(ValueUnion.psz, "full-iso") == 0)
1787 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_FULL_ISO;
1788 else if (strcmp(ValueUnion.psz, "long-iso") == 0)
1789 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LONG_ISO;
1790 else if (strcmp(ValueUnion.psz, "iso") == 0)
1791 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_ISO;
1792 else if (strcmp(ValueUnion.psz, "locale") == 0)
1793 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LOCALE;
1794 else if (*ValueUnion.psz == '+')
1795 {
1796 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_CUSTOM;
1797 Opts.pszTimeCustom = ValueUnion.psz;
1798 }
1799 else
1800 {
1801 Assert(!Opts.papCollections);
1802 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown sort by: %s", ValueUnion.psz);
1803 }
1804 break;
1805
1806 case 't':
1807 Opts.enmSort = RTCMDLSSORT_TIME;
1808 break;
1809
1810 case 'T':
1811 Opts.cchTab = ValueUnion.u8;
1812 break;
1813
1814 case 'u':
1815 Opts.enmTime = RTCMDLSTIME_ATIME;
1816 break;
1817
1818 case 'U':
1819 Opts.enmSort = RTCMDLSSORT_NONE;
1820 break;
1821
1822 case 'v':
1823 Opts.enmSort = RTCMDLSSORT_VERSION;
1824 break;
1825
1826 case 'w':
1827 Opts.cchWidth = ValueUnion.u32;
1828 break;
1829
1830 case 'x':
1831 Opts.enmFormat = RTCMDLSFORMAT_COLS_HORIZONTAL;
1832 break;
1833
1834 case 'X':
1835 Opts.enmSort = RTCMDLSSORT_EXTENSION;
1836 break;
1837
1838 case '1':
1839 Opts.enmFormat = RTCMDLSFORMAT_SINGLE;
1840 break;
1841
1842 case '?':
1843 RTPrintf("Usage: to be written\nOpts.on dump:\n");
1844 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1845 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1846 Assert(!Opts.papCollections);
1847 return RTEXITCODE_SUCCESS;
1848
1849 case 'V':
1850 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1851 Assert(!Opts.papCollections);
1852 return RTEXITCODE_SUCCESS;
1853
1854 default:
1855 Assert(!Opts.papCollections);
1856 return RTGetOptPrintError(chOpt, &ValueUnion);
1857 }
1858 }
1859}
1860
1861
1862int main(int argc, char **argv)
1863{
1864 int rc = RTR3InitExe(argc, &argv, 0);
1865 if (RT_FAILURE(rc))
1866 return RTMsgInitFailure(rc);
1867 return RTCmdLs(argc, argv);
1868}
1869
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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