VirtualBox

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

Last change on this file since 66850 was 66850, checked in by vboxsync, 8 years ago

Runtime: fall-thru

  • 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 66850 2017-05-09 14:29:29Z 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(); /* 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(); /* 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(); /* 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
1056 pCollection->papEntries[pCollection->cEntries++] = pEntry;
1057 pCollection->cbTotalAllocated += pEntry->Info.cbAllocated;
1058 pCollection->cbTotalFiles += pEntry->Info.cbObject;
1059 return RTEXITCODE_SUCCESS;
1060 }
1061 return RTMsgErrorExitFailure("Out of memory! (entry)");
1062}
1063
1064
1065/**
1066 * Checks if the entry is to be filtered out.
1067 *
1068 * @returns true if filtered out, false if included.
1069 * @param pOpts The options and state.
1070 * @param pszEntry The entry name.
1071 * @param pInfo The entry info.
1072 */
1073static bool rtCmdLsIsFilteredOut(PRTCMDLSOPTS pOpts, const char *pszEntry, PCRTFSOBJINFO pInfo)
1074{
1075 /*
1076 * Should we filter out this entry?
1077 */
1078 if ( !pOpts->fShowHidden
1079 && (pInfo->Attr.fMode & RTFS_DOS_HIDDEN))
1080 return true;
1081
1082 size_t const cchEntry = strlen(pszEntry);
1083 if ( !pOpts->fShowDotAndDotDot
1084 && cchEntry <= 2
1085 && pszEntry[0] == '.'
1086 && ( cchEntry == 1
1087 || pszEntry[1] == '.' ))
1088 return true;
1089
1090 if ( !pOpts->fShowBackups
1091 && pszEntry[cchEntry - 1] == '~')
1092 return true;
1093 return false;
1094}
1095
1096
1097/**
1098 * Processes a directory, recursing into subdirectories if desired.
1099 *
1100 * @returns Program exit code.
1101 * @param pOpts The options.
1102 * @param hVfsDir The directory.
1103 * @param pszPath Path buffer, RTPATH_MAX in size.
1104 * @param cchPath The length of the current path.
1105 * @param pInfo The parent information.
1106 */
1107static RTEXITCODE rtCmdLsProcessDirectory(PRTCMDLSOPTS pOpts, RTVFSDIR hVfsDir, char *pszPath, size_t cchPath, PCRTFSOBJINFO pInfo)
1108{
1109 /*
1110 * Create a new collection for this directory.
1111 */
1112 RT_NOREF(pInfo);
1113 PRTCMDLSCOLLECTION pCollection = rtCmdLsNewCollection(pOpts, pszPath);
1114 if (!pCollection)
1115 return RTEXITCODE_FAILURE;
1116
1117 /*
1118 * Process the directory entries.
1119 */
1120 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1121 size_t cbDirEntryAlloced = sizeof(RTDIRENTRYEX);
1122 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1123 if (!pDirEntry)
1124 return RTMsgErrorExitFailure("Out of memory! (direntry buffer)");
1125
1126 for (;;)
1127 {
1128 /*
1129 * Read the next entry.
1130 */
1131 size_t cbDirEntry = cbDirEntryAlloced;
1132 int rc = RTVfsDirReadEx(hVfsDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX);
1133 if (RT_FAILURE(rc))
1134 {
1135 if (rc == VERR_BUFFER_OVERFLOW)
1136 {
1137 RTMemTmpFree(pDirEntry);
1138 cbDirEntryAlloced = RT_ALIGN_Z(RT_MIN(cbDirEntry, cbDirEntryAlloced) + 64, 64);
1139 pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntryAlloced);
1140 if (pDirEntry)
1141 continue;
1142 rcExit = RTMsgErrorExitFailure("Out of memory (direntry buffer)");
1143 }
1144 else if (rc != VERR_NO_MORE_FILES)
1145 rcExit = RTMsgErrorExitFailure("RTVfsDirReadEx failed: %Rrc\n", rc);
1146 break;
1147 }
1148
1149 /*
1150 * Process the entry.
1151 */
1152 if (rtCmdLsIsFilteredOut(pOpts, pDirEntry->szName, &pDirEntry->Info))
1153 continue;
1154 RTEXITCODE rcExit2 = rtCmdLsAddOne(pCollection, pDirEntry->szName, &pDirEntry->Info);
1155 if (rcExit2 != RTEXITCODE_SUCCESS)
1156 rcExit = rcExit2;
1157 }
1158
1159 RTMemTmpFree(pDirEntry);
1160
1161 /*
1162 * Recurse into subdirectories if requested.
1163 */
1164 if (pOpts->fRecursive)
1165 {
1166 for (uint32_t i = 0; i < pCollection->cEntries; i++)
1167 {
1168 PRTCMDLSENTRY pEntry = pCollection->papEntries[i];
1169 if (RTFS_IS_SYMLINK(pEntry->Info.Attr.fMode))
1170 {
1171 if (!pOpts->fFollowSymlinksInDirs)
1172 continue;
1173 /** @todo implement following symbolic links in the tree. */
1174 continue;
1175 }
1176 else if ( !RTFS_IS_DIRECTORY(pEntry->Info.Attr.fMode)
1177 || ( pEntry->szName[0] == '.'
1178 && ( pEntry->szName[1] == '\0'
1179 || ( pEntry->szName[1] == '.'
1180 && pEntry->szName[2] == '\0'))) )
1181 continue;
1182
1183 /* Open subdirectory and process it. */
1184 RTVFSDIR hSubDir;
1185 int rc = RTVfsDirOpenDir(hVfsDir, pEntry->szName, 0 /*fFlags*/, &hSubDir);
1186 if (RT_SUCCESS(rc))
1187 {
1188 if (cchPath + 1 + pEntry->cchName + 1 < RTPATH_MAX)
1189 {
1190 pszPath[cchPath] = RTPATH_SLASH;
1191 memcpy(&pszPath[cchPath + 1], pEntry->szName, pEntry->cchName + 1);
1192 RTEXITCODE rcExit2 = rtCmdLsProcessDirectory(pOpts, hVfsDir, pszPath,
1193 cchPath + 1 + pEntry->cchName, &pEntry->Info);
1194 if (rcExit2 != RTEXITCODE_SUCCESS)
1195 rcExit = rcExit2;
1196 pszPath[cchPath] = '\0';
1197 }
1198 else
1199 rcExit = RTMsgErrorExitFailure("Too deep recursion: %s%c%s", pszPath, RTPATH_SLASH, pEntry->szName);
1200 }
1201 else
1202 rcExit = RTMsgErrorExitFailure("RTVfsDirOpenDir failed on %s in %s: %Rrc\n", pEntry->szName, pszPath, rc);
1203 }
1204 }
1205 return rcExit;
1206}
1207
1208
1209/**
1210 * Processes one argument.
1211 *
1212 * @returns Program exit code.
1213 * @param pOpts The options.
1214 * @param pszArg The argument.
1215 */
1216static RTEXITCODE rtCmdLsProcessArgument(PRTCMDLSOPTS pOpts, const char *pszArg)
1217{
1218 /*
1219 * Query info about the object 'pszArg' indicates.
1220 */
1221 RTERRINFOSTATIC ErrInfo;
1222 uint32_t offError;
1223 RTFSOBJINFO Info;
1224 uint32_t fPath = pOpts->fFollowSymlinkToAnyArgs ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK;
1225 int rc = RTVfsChainQueryInfo(pszArg, &Info, RTFSOBJATTRADD_UNIX, fPath, &offError, RTErrInfoInitStatic(&ErrInfo));
1226 if (RT_FAILURE(rc))
1227 return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszArg, rc, offError, &ErrInfo.Core);
1228
1229 /* Symbolic links requires special handling of course. */
1230 if (RTFS_IS_SYMLINK(Info.Attr.fMode))
1231 {
1232 if (pOpts->fFollowSymlinkToDirArgs)
1233 {
1234 RTFSOBJINFO Info2;
1235 rc = RTVfsChainQueryInfo(pszArg, &Info2, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK,
1236 &offError, RTErrInfoInitStatic(&ErrInfo));
1237 if (RT_SUCCESS(rc) && !RTFS_IS_DIRECTORY(Info.Attr.fMode))
1238 Info = Info2;
1239 }
1240 }
1241
1242 /*
1243 * If it's not a directory or we've been told to process directories
1244 * without going into them, just add it to the default collection.
1245 */
1246 if ( !pOpts->fFollowDirectoryArgs
1247 || !RTFS_IS_DIRECTORY(Info.Attr.fMode))
1248 {
1249 if ( pOpts->cCollections > 0
1250 || rtCmdLsNewCollection(pOpts, "") != NULL)
1251 return rtCmdLsAddOne(pOpts->papCollections[0], pszArg, &Info);
1252 return RTEXITCODE_FAILURE;
1253 }
1254
1255 /*
1256 * Open the directory.
1257 */
1258 RTVFSDIR hVfsDir;
1259 rc = RTVfsChainOpenDir(pszArg, 0 /*fFlags*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo));
1260 if (RT_FAILURE(rc))
1261 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenDir", pszArg, rc, offError, &ErrInfo.Core);
1262
1263 RTEXITCODE rcExit;
1264 char szPath[RTPATH_MAX];
1265 size_t cchPath = strlen(pszArg);
1266 if (cchPath < sizeof(szPath))
1267 {
1268 memcpy(szPath, pszArg, cchPath + 1);
1269 rcExit = rtCmdLsProcessDirectory(pOpts, hVfsDir, szPath, cchPath, &Info);
1270 }
1271 else
1272 rcExit = RTMsgErrorExitFailure("Too long argument: %s", pszArg);
1273 RTVfsDirRelease(hVfsDir);
1274 return rcExit;
1275}
1276
1277
1278/**
1279 * A /bin/cat clone.
1280 *
1281 * @returns Program exit code.
1282 *
1283 * @param cArgs The number of arguments.
1284 * @param papszArgs The argument vector. (Note that this may be
1285 * reordered, so the memory must be writable.)
1286 */
1287RTEXITCODE RTCmdLs(unsigned cArgs, char **papszArgs)
1288{
1289
1290 /*
1291 * Parse the command line.
1292 */
1293#define OPT_AUTHOR 1000
1294#define OPT_BLOCK_SIZE 1001
1295#define OPT_COLOR 1002
1296#define OPT_FILE_TYPE 1003
1297#define OPT_FORMAT 1004
1298#define OPT_FULL_TIME 1005
1299#define OPT_GROUP_DIRECTORIES_FIRST 1006
1300#define OPT_SI 1007
1301#define OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR 1008
1302#define OPT_HIDE 1009
1303#define OPT_INDICATOR_STYLE 1010
1304#define OPT_MACHINE_READABLE 1011
1305#define OPT_SHOW_CONTROL_CHARS 1012
1306#define OPT_QUOTING_STYLE 1013
1307#define OPT_SORT 1014
1308#define OPT_TIME 1015
1309#define OPT_TIME_STYLE 1016
1310 static const RTGETOPTDEF s_aOptions[] =
1311 {
1312 { "--all", 'a', RTGETOPT_REQ_NOTHING },
1313 { "--almost-all", 'A', RTGETOPT_REQ_NOTHING },
1314 //{ "--author", OPT_AUTHOR, RTGETOPT_REQ_NOTHING },
1315 { "--escape", 'b', RTGETOPT_REQ_NOTHING },
1316 { "--block-size", OPT_BLOCK_SIZE, RTGETOPT_REQ_UINT32 },
1317 { "--ctime", 'c', RTGETOPT_REQ_NOTHING },
1318 //{ "--columns", 'C', RTGETOPT_REQ_NOTHING },
1319 //{ "--color", OPT_COLOR, RTGETOPT_OPT_STRING },
1320 { "--directory", 'd', RTGETOPT_REQ_NOTHING },
1321 //{ "--dired", 'D', RTGETOPT_REQ_NOTHING },
1322 { "--dash-f", 'f', RTGETOPT_REQ_NOTHING },
1323 //{ "--classify", 'F', RTGETOPT_REQ_NOTHING },
1324 //{ "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_NOTHING },
1325 { "--format", OPT_FORMAT, RTGETOPT_REQ_STRING },
1326 { "--full-time", OPT_FULL_TIME, RTGETOPT_REQ_NOTHING },
1327 { "--dash-g", 'g', RTGETOPT_REQ_NOTHING },
1328 { "--group-directories-first", OPT_GROUP_DIRECTORIES_FIRST, RTGETOPT_REQ_NOTHING },
1329 { "--no-group", 'G', RTGETOPT_REQ_NOTHING },
1330 { "--human-readable", 'h', RTGETOPT_REQ_NOTHING },
1331 { "--si", OPT_SI, RTGETOPT_REQ_NOTHING },
1332 { "--dereference-command-line", 'H', RTGETOPT_REQ_NOTHING },
1333 { "--dereference-command-line-symlink-to-dir", OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR, RTGETOPT_REQ_NOTHING },
1334 //{ "--hide" OPT_HIDE, RTGETOPT_REQ_STRING },
1335 //{ "--indicator-style" OPT_INDICATOR_STYLE, RTGETOPT_REQ_STRING },
1336 { "--inode", 'i', RTGETOPT_REQ_NOTHING },
1337 { "--block-size-1kib", 'k', RTGETOPT_REQ_NOTHING },
1338 { "--long", 'l', RTGETOPT_REQ_NOTHING },
1339 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1340 { "--format-commas", 'm', RTGETOPT_REQ_NOTHING },
1341 { "--machinereadable", OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1342 { "--machine-readable", OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1343 { "--numeric-uid-gid", 'n', RTGETOPT_REQ_NOTHING },
1344 { "--literal", 'N', RTGETOPT_REQ_NOTHING },
1345 { "--long-without-group-info", 'o', RTGETOPT_REQ_NOTHING },
1346 //{ "--indicator-style", 'p', RTGETOPT_REQ_STRING },
1347 { "--hide-control-chars", 'q', RTGETOPT_REQ_NOTHING },
1348 { "--show-control-chars", OPT_SHOW_CONTROL_CHARS, RTGETOPT_REQ_NOTHING },
1349 //{ "--quote-name", 'Q', RTGETOPT_REQ_NOTHING },
1350 //{ "--quoting-style", OPT_QUOTING_STYLE, RTGETOPT_REQ_STRING },
1351 { "--reverse", 'r', RTGETOPT_REQ_NOTHING },
1352 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1353 { "--size", 's', RTGETOPT_REQ_NOTHING },
1354 { "--sort-by-size", 'S', RTGETOPT_REQ_NOTHING },
1355 { "--sort", OPT_SORT, RTGETOPT_REQ_STRING },
1356 { "--time", OPT_TIME, RTGETOPT_REQ_STRING },
1357 { "--time-style", OPT_TIME_STYLE, RTGETOPT_REQ_STRING },
1358 { "--sort-by-time", 't', RTGETOPT_REQ_NOTHING },
1359 { "--tabsize", 'T', RTGETOPT_REQ_UINT8 },
1360 { "--atime", 'u', RTGETOPT_REQ_NOTHING },
1361 { "--unsorted", 'U', RTGETOPT_REQ_NOTHING },
1362 { "--version-sort", 'v', RTGETOPT_REQ_NOTHING },
1363 { "--width", 'w', RTGETOPT_REQ_UINT32 },
1364 { "--list-by-line", 'x', RTGETOPT_REQ_NOTHING },
1365 { "--sort-by-extension", 'X', RTGETOPT_REQ_NOTHING },
1366 { "--one-file-per-line", '1', RTGETOPT_REQ_NOTHING },
1367 { "--help", '?', RTGETOPT_REQ_NOTHING },
1368 };
1369
1370 RTCMDLSOPTS Opts;
1371 Opts.fFollowSymlinksInDirs = false;
1372 Opts.fFollowSymlinkToAnyArgs = false;
1373 Opts.fFollowSymlinkToDirArgs = false;
1374 Opts.fFollowDirectoryArgs = true;
1375 Opts.fRecursive = false;
1376 Opts.fShowHidden = false;
1377 Opts.fShowDotAndDotDot = false;
1378 Opts.fShowBackups = true;
1379 Opts.enmSort = RTCMDLSSORT_NAME;
1380 Opts.fReverseSort = false;
1381 Opts.fGroupDirectoriesFirst = false;
1382 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1383 Opts.fEscapeNonGraphicChars = false;
1384 Opts.fEscapeControlChars = true;
1385 Opts.fHideControlChars = false;
1386 Opts.fHumanReadableSizes = false; /**< -h */
1387 Opts.fSiUnits = false;
1388 Opts.cbBlock = 0;
1389 Opts.fShowOwner = true;
1390 Opts.fShowGroup = true;
1391 Opts.fNumericalIds = false;
1392 Opts.fShowINode = false;
1393 Opts.fShowAllocatedSize = false;
1394 Opts.cchTab = 8;
1395 Opts.cchWidth = 80;
1396 Opts.enmColor = RTCMDLSCOLOR_NONE;
1397 Opts.enmTime = RTCMDLSTIME_MTIME;
1398 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LOCALE;
1399 Opts.pszTimeCustom = NULL;
1400
1401 Opts.cCollections = 0;
1402 Opts.cCollectionsAllocated = 0;
1403 Opts.papCollections = NULL;
1404
1405
1406 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1407 unsigned cProcessed = 0;
1408 RTVFSIOSTREAM hVfsOutput = NIL_RTVFSIOSTREAM;
1409 int rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
1410 true /*fLeaveOpen*/, &hVfsOutput);
1411 if (RT_FAILURE(rc))
1412 return RTMsgErrorExitFailure("RTVfsIoStrmFromStdHandle: %Rrc", rc);
1413
1414 RTGETOPTSTATE GetState;
1415 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1416 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1417 if (RT_FAILURE(rc))
1418 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1419
1420 for (;;)
1421 {
1422 RTGETOPTUNION ValueUnion;
1423 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1424 switch (chOpt)
1425 {
1426 case 0:
1427 /* When reaching the end of arguments without having processed any
1428 files/dirs/whatever yet, we do the current directory. */
1429 if (cProcessed > 0)
1430 {
1431 RTEXITCODE rcExit2 = rtCmdLsDisplayCollections(&Opts);
1432 if (rcExit2 != RTEXITCODE_SUCCESS)
1433 rcExit = rcExit2;
1434 rtCmdLsFreeCollections(&Opts);
1435 return rcExit;
1436 }
1437 ValueUnion.psz = ".";
1438 /* Fall thru. */
1439 case VINF_GETOPT_NOT_OPTION:
1440 {
1441 RTEXITCODE rcExit2 = rtCmdLsProcessArgument(&Opts, ValueUnion.psz);
1442 if (rcExit2 != RTEXITCODE_SUCCESS)
1443 rcExit = rcExit2;
1444 cProcessed++;
1445 break;
1446 }
1447
1448 case 'a':
1449 Opts.fShowHidden = true;
1450 Opts.fShowDotAndDotDot = true;
1451 break;
1452
1453 case 'A':
1454 Opts.fShowHidden = true;
1455 Opts.fShowDotAndDotDot = false;
1456 break;
1457
1458 case 'b':
1459 Opts.fEscapeNonGraphicChars = true;
1460 break;
1461
1462 case OPT_BLOCK_SIZE:
1463 if (!ValueUnion.u32)
1464 {
1465 Assert(!Opts.papCollections);
1466 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid block size: %u", ValueUnion.u32);
1467 }
1468 Opts.cbBlock = ValueUnion.u32;
1469 Opts.fHumanReadableSizes = false;
1470 Opts.fSiUnits = false;
1471 break;
1472
1473 case 'c':
1474 Opts.enmTime = RTCMDLSTIME_CTIME;
1475 break;
1476
1477 case 'C':
1478 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1479 break;
1480
1481 case 'd':
1482 Opts.fFollowDirectoryArgs = false;
1483 Opts.fFollowSymlinkToAnyArgs = false;
1484 Opts.fFollowSymlinkToDirArgs = false;
1485 Opts.fRecursive = false;
1486 break;
1487
1488 case 'f':
1489 Opts.fShowHidden = true;
1490 Opts.fShowDotAndDotDot = true;
1491 if (Opts.enmFormat == RTCMDLSFORMAT_LONG)
1492 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1493 Opts.enmColor = RTCMDLSCOLOR_NONE;
1494 Opts.enmSort = RTCMDLSSORT_NONE;
1495 break;
1496
1497 case OPT_FORMAT:
1498 if ( strcmp(ValueUnion.psz, "across") == 0
1499 || strcmp(ValueUnion.psz, "horizontal") == 0)
1500 Opts.enmFormat = RTCMDLSFORMAT_COLS_HORIZONTAL;
1501 else if (strcmp(ValueUnion.psz, "commas") == 0)
1502 Opts.enmFormat = RTCMDLSFORMAT_COMMAS;
1503 else if ( strcmp(ValueUnion.psz, "long") == 0
1504 || strcmp(ValueUnion.psz, "verbose") == 0)
1505 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1506 else if (strcmp(ValueUnion.psz, "single-column") == 0)
1507 Opts.enmFormat = RTCMDLSFORMAT_SINGLE;
1508 else if (strcmp(ValueUnion.psz, "vertical") == 0)
1509 Opts.enmFormat = RTCMDLSFORMAT_COLS_VERTICAL;
1510 else if (strcmp(ValueUnion.psz, "machine-readable") == 0)
1511 Opts.enmFormat = RTCMDLSFORMAT_MACHINE_READABLE;
1512 else
1513 {
1514 Assert(!Opts.papCollections);
1515 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown format: %s", ValueUnion.psz);
1516 }
1517 break;
1518
1519 case OPT_FULL_TIME:
1520 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1521 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_FULL_ISO;
1522 break;
1523
1524 case 'g':
1525 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1526 Opts.fShowOwner = false;
1527 break;
1528
1529 case OPT_GROUP_DIRECTORIES_FIRST:
1530 Opts.fGroupDirectoriesFirst = true;
1531 break;
1532
1533 case 'G':
1534 Opts.fShowGroup = false;
1535 break;
1536
1537 case 'h':
1538 Opts.fHumanReadableSizes = true;
1539 Opts.fSiUnits = false;
1540 break;
1541
1542 case OPT_SI:
1543 Opts.fHumanReadableSizes = true;
1544 Opts.fSiUnits = true;
1545 break;
1546
1547 case 'H':
1548 Opts.fFollowSymlinkToAnyArgs = true;
1549 Opts.fFollowSymlinkToDirArgs = true;
1550 break;
1551
1552 case OPT_DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR:
1553 Opts.fFollowSymlinkToAnyArgs = false;
1554 Opts.fFollowSymlinkToDirArgs = true;
1555 break;
1556
1557 case 'i':
1558 Opts.fShowINode = true;
1559 break;
1560
1561 case 'k':
1562 Opts.cbBlock = _1K;
1563 Opts.fHumanReadableSizes = false;
1564 Opts.fSiUnits = false;
1565 break;
1566
1567 case 'l':
1568 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1569 break;
1570
1571 case 'L':
1572 Opts.fFollowSymlinksInDirs = true;
1573 Opts.fFollowSymlinkToAnyArgs = true;
1574 Opts.fFollowSymlinkToDirArgs = true;
1575 break;
1576
1577 case 'm':
1578 Opts.enmFormat = RTCMDLSFORMAT_COMMAS;
1579 break;
1580
1581 case OPT_MACHINE_READABLE:
1582 Opts.enmFormat = RTCMDLSFORMAT_MACHINE_READABLE;
1583 break;
1584
1585 case 'n':
1586 Opts.fNumericalIds = true;
1587 break;
1588
1589 case 'N':
1590 Opts.fEscapeNonGraphicChars = false;
1591 Opts.fEscapeControlChars = false;
1592 Opts.fHideControlChars = false;
1593 break;
1594
1595 case 'o':
1596 Opts.enmFormat = RTCMDLSFORMAT_LONG;
1597 Opts.fShowGroup = false;
1598 break;
1599
1600 case 'q':
1601 Opts.fHideControlChars = true;
1602 break;
1603
1604 case OPT_SHOW_CONTROL_CHARS:
1605 Opts.fHideControlChars = true;
1606 break;
1607
1608 case 'r':
1609 Opts.fReverseSort = true;
1610 break;
1611
1612 case 'R':
1613 Opts.fRecursive = true;
1614 break;
1615
1616 case 's':
1617 Opts.fShowAllocatedSize = true;
1618 break;
1619
1620 case 'S':
1621 Opts.enmSort = RTCMDLSSORT_SIZE;
1622 break;
1623
1624 case OPT_SORT:
1625 if (strcmp(ValueUnion.psz, "none") == 0)
1626 Opts.enmSort = RTCMDLSSORT_NONE;
1627 else if (strcmp(ValueUnion.psz, "extension") == 0)
1628 Opts.enmSort = RTCMDLSSORT_EXTENSION;
1629 else if (strcmp(ValueUnion.psz, "size") == 0)
1630 Opts.enmSort = RTCMDLSSORT_SIZE;
1631 else if (strcmp(ValueUnion.psz, "time") == 0)
1632 Opts.enmSort = RTCMDLSSORT_TIME;
1633 else if (strcmp(ValueUnion.psz, "version") == 0)
1634 Opts.enmSort = RTCMDLSSORT_VERSION;
1635 else
1636 {
1637 Assert(!Opts.papCollections);
1638 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown sort by: %s", ValueUnion.psz);
1639 }
1640 break;
1641
1642 case OPT_TIME:
1643 if ( strcmp(ValueUnion.psz, "btime") == 0
1644 || strcmp(ValueUnion.psz, "birth") == 0)
1645 Opts.enmTime = RTCMDLSTIME_BTIME;
1646 else if ( strcmp(ValueUnion.psz, "ctime") == 0
1647 || strcmp(ValueUnion.psz, "status") == 0)
1648 Opts.enmTime = RTCMDLSTIME_CTIME;
1649 else if ( strcmp(ValueUnion.psz, "mtime") == 0
1650 || strcmp(ValueUnion.psz, "write") == 0
1651 || strcmp(ValueUnion.psz, "modify") == 0)
1652 Opts.enmTime = RTCMDLSTIME_MTIME;
1653 else if ( strcmp(ValueUnion.psz, "atime") == 0
1654 || strcmp(ValueUnion.psz, "access") == 0
1655 || strcmp(ValueUnion.psz, "use") == 0)
1656 Opts.enmTime = RTCMDLSTIME_ATIME;
1657 else
1658 {
1659 Assert(!Opts.papCollections);
1660 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown time attribute: %s", ValueUnion.psz);
1661 }
1662 break;
1663
1664 case OPT_TIME_STYLE:
1665 if (strcmp(ValueUnion.psz, "full-iso") == 0)
1666 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_FULL_ISO;
1667 else if (strcmp(ValueUnion.psz, "long-iso") == 0)
1668 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LONG_ISO;
1669 else if (strcmp(ValueUnion.psz, "iso") == 0)
1670 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_ISO;
1671 else if (strcmp(ValueUnion.psz, "locale") == 0)
1672 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_LOCALE;
1673 else if (*ValueUnion.psz == '+')
1674 {
1675 Opts.enmTimeStyle = RTCMDLSTIMESTYLE_CUSTOM;
1676 Opts.pszTimeCustom = ValueUnion.psz;
1677 }
1678 else
1679 {
1680 Assert(!Opts.papCollections);
1681 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown sort by: %s", ValueUnion.psz);
1682 }
1683 break;
1684
1685 case 't':
1686 Opts.enmSort = RTCMDLSSORT_TIME;
1687 break;
1688
1689 case 'T':
1690 Opts.cchTab = ValueUnion.u8;
1691 break;
1692
1693 case 'u':
1694 Opts.enmTime = RTCMDLSTIME_ATIME;
1695 break;
1696
1697 case 'U':
1698 Opts.enmSort = RTCMDLSSORT_NONE;
1699 break;
1700
1701 case 'v':
1702 Opts.enmSort = RTCMDLSSORT_VERSION;
1703 break;
1704
1705 case 'w':
1706 Opts.cchWidth = ValueUnion.u32;
1707 break;
1708
1709 case 'x':
1710 Opts.enmFormat = RTCMDLSFORMAT_COLS_HORIZONTAL;
1711 break;
1712
1713 case 'X':
1714 Opts.enmSort = RTCMDLSSORT_EXTENSION;
1715 break;
1716
1717 case '1':
1718 Opts.enmFormat = RTCMDLSFORMAT_SINGLE;
1719 break;
1720
1721 case '?':
1722 RTPrintf("Usage: to be written\nOpts.on dump:\n");
1723 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1724 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1725 Assert(!Opts.papCollections);
1726 return RTEXITCODE_SUCCESS;
1727
1728 case 'V':
1729 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1730 Assert(!Opts.papCollections);
1731 return RTEXITCODE_SUCCESS;
1732
1733 default:
1734 Assert(!Opts.papCollections);
1735 return RTGetOptPrintError(chOpt, &ValueUnion);
1736 }
1737 }
1738}
1739
1740
1741int main(int argc, char **argv)
1742{
1743 int rc = RTR3InitExe(argc, &argv, 0);
1744 if (RT_FAILURE(rc))
1745 return RTMsgInitFailure(rc);
1746 return RTCmdLs(argc, argv);
1747}
1748
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