VirtualBox

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

Last change on this file since 69287 was 69051, checked in by vboxsync, 7 years ago

RTLs.cpp: Recursion fix

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

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