2 * CMD - Wine-compatible command line interface - Directory functions.
4 * Copyright (C) 1999 D A Pickles
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * On entry, global variables quals, param1, param2 contain
24 * the qualifiers (uppercased and concatenated) and parameters entered, with
25 * environment-variable and batch parameter substitution already done.
28 #define WIN32_LEAN_AND_MEAN
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(cmd
);
35 int WCMD_dir_sort (const void *a
, const void *b
);
36 char * WCMD_filesize64 (ULONGLONG free
);
37 char * WCMD_strrev (char *buff
);
38 static void WCMD_getfileowner(char *filename
, char *owner
, int ownerlen
);
39 static void WCMD_dir_trailer(char drive
);
42 extern char quals
[MAX_PATH
], param1
[MAX_PATH
], param2
[MAX_PATH
];
43 extern DWORD errorlevel
;
45 typedef enum _DISPLAYTIME
52 typedef enum _DISPLAYORDER
60 static DIRECTORY_STACK
*WCMD_list_directory (DIRECTORY_STACK
*parms
, int level
);
61 static int file_total
, dir_total
, recurse
, wide
, bare
, max_width
, lower
;
62 static int shortname
, usernames
;
63 static ULONGLONG byte_total
;
64 static DISPLAYTIME dirTime
;
65 static DISPLAYORDER dirOrder
;
66 static BOOL orderReverse
, orderGroupDirs
, orderGroupDirsReverse
, orderByCol
;
67 static BOOL separator
;
68 static ULONG showattrs
, attrsbits
;
70 /*****************************************************************************
73 * List a file directory.
77 void WCMD_directory (char *cmd
) {
79 char path
[MAX_PATH
], cwd
[MAX_PATH
];
80 int status
, paged_mode
;
81 CONSOLE_SCREEN_BUFFER_INFO consoleInfo
;
83 char string
[MAXSTRING
];
85 int argsProcessed
= 0;
88 BOOL trailerReqd
= FALSE
;
89 DIRECTORY_STACK
*fullParms
= NULL
;
90 DIRECTORY_STACK
*prevEntry
= NULL
;
91 DIRECTORY_STACK
*thisEntry
= NULL
;
99 /* Prefill Quals with (uppercased) DIRCMD env var */
100 if (GetEnvironmentVariable ("DIRCMD", string
, sizeof(string
))) {
102 while ( (*p
= toupper(*p
)) ) ++p
;
103 strcat(string
,quals
);
104 strcpy(quals
, string
);
108 file_total
= dir_total
= 0;
110 /* Initialize all flags to their defaults as if no DIRCMD or quals */
122 orderReverse
= FALSE
;
123 orderGroupDirs
= FALSE
;
124 orderGroupDirsReverse
= FALSE
;
128 /* Handle args - Loop through so right most is the effective one */
129 /* Note: /- appears to be a negate rather than an off, eg. dir
130 /-W is wide, or dir /w /-w /-w is also wide */
132 while (*p
&& (*p
=='/' || *p
==' ')) {
134 if (*p
++==' ') continue; /* Skip / and blanks introduced through DIRCMD */
141 WINE_TRACE("Processing arg '%c' (in %s)\n", *p
, quals
);
143 case 'P': if (negate
) paged_mode
= !paged_mode
;
144 else paged_mode
= TRUE
;
146 case 'S': if (negate
) recurse
= !recurse
;
149 case 'W': if (negate
) wide
= !wide
;
152 case 'B': if (negate
) bare
= !bare
;
155 case 'L': if (negate
) lower
= !lower
;
158 case 'X': if (negate
) shortname
= !shortname
;
159 else shortname
= TRUE
;
161 case 'Q': if (negate
) usernames
= !usernames
;
162 else usernames
= TRUE
;
164 case 'D': if (negate
) orderByCol
= !orderByCol
;
165 else orderByCol
= TRUE
;
167 case 'C': if (negate
) separator
= !separator
;
168 else separator
= TRUE
;
171 if (*p
==':') p
++; /* Skip optional : */
173 if (*p
== 'A') dirTime
= Access
;
174 else if (*p
== 'C') dirTime
= Creation
;
175 else if (*p
== 'W') dirTime
= Written
;
177 /* Support /T and /T: with no parms, default to written */
178 else if (*p
== 0x00 || *p
== '/') {
180 p
= p
- 1; /* So when step on, move to '/' */
182 SetLastError(ERROR_INVALID_PARAMETER
);
189 if (*p
==':') p
++; /* Skip optional : */
190 while (*p
&& *p
!= '/') {
191 WINE_TRACE("Processing subparm '%c' (in %s)\n", *p
, quals
);
193 case 'N': dirOrder
= Name
; break;
194 case 'E': dirOrder
= Extension
; break;
195 case 'S': dirOrder
= Size
; break;
196 case 'D': dirOrder
= Date
; break;
197 case '-': if (*(p
+1)=='G') orderGroupDirsReverse
=TRUE
;
198 else orderReverse
= TRUE
;
200 case 'G': orderGroupDirs
= TRUE
; break;
202 SetLastError(ERROR_INVALID_PARAMETER
);
209 p
= p
- 1; /* So when step on, move to '/' */
214 if (*p
==':') p
++; /* Skip optional : */
215 while (*p
&& *p
!= '/') {
216 BOOL anegate
= FALSE
;
219 /* Note /A: - options are 'offs' not toggles */
225 WINE_TRACE("Processing subparm '%c' (in %s)\n", *p
, quals
);
227 case 'D': mask
= FILE_ATTRIBUTE_DIRECTORY
; break;
228 case 'H': mask
= FILE_ATTRIBUTE_HIDDEN
; break;
229 case 'S': mask
= FILE_ATTRIBUTE_SYSTEM
; break;
230 case 'R': mask
= FILE_ATTRIBUTE_READONLY
; break;
231 case 'A': mask
= FILE_ATTRIBUTE_ARCHIVE
; break;
233 SetLastError(ERROR_INVALID_PARAMETER
);
239 /* Keep running list of bits we care about */
242 /* Mask shows what MUST be in the bits we care about */
243 if (anegate
) showattrs
= showattrs
& ~mask
;
244 else showattrs
|= mask
;
248 p
= p
- 1; /* So when step on, move to '/' */
249 WINE_TRACE("Result: showattrs %x, bits %x\n", showattrs
, attrsbits
);
252 SetLastError(ERROR_INVALID_PARAMETER
);
260 /* Handle conflicting args and initialization */
261 if (bare
|| shortname
) wide
= FALSE
;
262 if (bare
) shortname
= FALSE
;
263 if (wide
) usernames
= FALSE
;
264 if (orderByCol
) wide
= TRUE
;
267 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE
), &consoleInfo
))
268 max_width
= consoleInfo
.dwSize
.X
;
273 WCMD_enter_paged_mode(NULL
);
279 GetCurrentDirectory (1024, cwd
);
282 /* Loop through all args, calculating full effective directory */
286 char fullname
[MAXSTRING
];
287 char *thisArg
= WCMD_parameter (cmd
, argno
++, &argN
);
288 if (argN
&& argN
[0] != '/') {
290 WINE_TRACE("Found parm '%s'\n", thisArg
);
291 if (thisArg
[1] == ':' && thisArg
[2] == '\\') {
292 strcpy(fullname
, thisArg
);
293 } else if (thisArg
[1] == ':' && thisArg
[2] != '\\') {
295 sprintf(envvar
, "=%c:", thisArg
[0]);
296 if (!GetEnvironmentVariable(envvar
, fullname
, MAX_PATH
)) {
297 sprintf(fullname
, "%c:", thisArg
[0]);
299 strcat(fullname
, "\\");
300 strcat(fullname
, &thisArg
[2]);
301 } else if (thisArg
[0] == '\\') {
302 strncpy(fullname
, cwd
, 2);
304 strcat((fullname
+2), thisArg
);
306 strcpy(fullname
, cwd
);
307 strcat(fullname
, thisArg
);
309 WINE_TRACE("Using location '%s'\n", fullname
);
311 status
= GetFullPathName (fullname
, sizeof(path
), path
, NULL
);
314 * If the path supplied does not include a wildcard, and the endpoint of the
315 * path references a directory, we need to list the *contents* of that
316 * directory not the directory file itself.
318 if ((strchr(path
, '*') == NULL
) && (strchr(path
, '%') == NULL
)) {
319 status
= GetFileAttributes (path
);
320 if ((status
!= INVALID_FILE_ATTRIBUTES
) && (status
& FILE_ATTRIBUTE_DIRECTORY
)) {
321 if (path
[strlen(path
)-1] == '\\') {
325 strcat (path
, "\\*");
329 /* Special case wildcard search with no extension (ie parameters ending in '.') as
330 GetFullPathName strips off the additional '.' */
331 if (fullname
[strlen(fullname
)-1] == '.') strcat(path
, ".");
334 WINE_TRACE("Using path '%s'\n", path
);
335 thisEntry
= (DIRECTORY_STACK
*) HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK
));
336 if (fullParms
== NULL
) fullParms
= thisEntry
;
337 if (prevEntry
!= NULL
) prevEntry
->next
= thisEntry
;
338 prevEntry
= thisEntry
;
339 thisEntry
->next
= NULL
;
341 /* Split into components */
342 WCMD_splitpath(path
, drive
, dir
, fname
, ext
);
343 WINE_TRACE("Path Parts: drive: '%s' dir: '%s' name: '%s' ext:'%s'\n",
344 drive
, dir
, fname
, ext
);
346 thisEntry
->dirName
= HeapAlloc(GetProcessHeap(),0,strlen(drive
)+strlen(dir
)+1);
347 strcpy(thisEntry
->dirName
, drive
);
348 strcat(thisEntry
->dirName
, dir
);
350 thisEntry
->fileName
= HeapAlloc(GetProcessHeap(),0,strlen(fname
)+strlen(ext
)+1);
351 strcpy(thisEntry
->fileName
, fname
);
352 strcat(thisEntry
->fileName
, ext
);
357 /* If just 'dir' entered, a '*' parameter is assumed */
358 if (fullParms
== NULL
) {
359 WINE_TRACE("Inserting default '*'\n");
360 fullParms
= (DIRECTORY_STACK
*) HeapAlloc(GetProcessHeap(),0, sizeof(DIRECTORY_STACK
));
361 fullParms
->next
= NULL
;
362 fullParms
->dirName
= HeapAlloc(GetProcessHeap(),0,(strlen(cwd
)+1));
363 strcpy(fullParms
->dirName
, cwd
);
364 fullParms
->fileName
= HeapAlloc(GetProcessHeap(),0,2);
365 strcpy(fullParms
->fileName
, "*");
370 thisEntry
= fullParms
;
373 while (thisEntry
!= NULL
) {
375 /* Output disk free (trailer) and volume information (header) if the drive
377 if (lastDrive
!= toupper(thisEntry
->dirName
[0])) {
379 /* Trailer Information */
380 if (lastDrive
!= '?') {
382 WCMD_dir_trailer(prevEntry
->dirName
[0]);
385 lastDrive
= toupper(thisEntry
->dirName
[0]);
390 WINE_TRACE("Writing volume for '%c:'\n", thisEntry
->dirName
[0]);
391 strncpy(drive
, thisEntry
->dirName
, 2);
393 status
= WCMD_volume (0, drive
);
401 if (!bare
) WCMD_output ("\n\n");
404 /* Clear any errors from previous invocations, and process it */
406 prevEntry
= thisEntry
;
407 thisEntry
= WCMD_list_directory (thisEntry
, 0);
410 /* Trailer Information */
412 WCMD_dir_trailer(prevEntry
->dirName
[0]);
416 if (paged_mode
) WCMD_leave_paged_mode();
418 /* Free storage allocated for parms */
419 while (fullParms
!= NULL
) {
420 prevEntry
= fullParms
;
421 fullParms
= prevEntry
->next
;
422 HeapFree(GetProcessHeap(),0,prevEntry
->dirName
);
423 HeapFree(GetProcessHeap(),0,prevEntry
->fileName
);
424 HeapFree(GetProcessHeap(),0,prevEntry
);
428 /*****************************************************************************
429 * WCMD_list_directory
431 * List a single file directory. This function (and those below it) can be called
432 * recursively when the /S switch is used.
434 * FIXME: Assumes 24-line display for the /P qualifier.
437 static DIRECTORY_STACK
*WCMD_list_directory (DIRECTORY_STACK
*inputparms
, int level
) {
439 char string
[1024], datestring
[32], timestring
[32];
440 char real_path
[MAX_PATH
];
445 int dir_count
, file_count
, entry_count
, i
, widest
, cur_width
, tmp_width
;
446 int numCols
, numRows
;
448 ULARGE_INTEGER byte_count
, file_size
;
449 DIRECTORY_STACK
*parms
;
450 int concurrentDirs
= 0;
451 BOOL done_header
= FALSE
;
457 byte_count
.QuadPart
= 0;
461 /* Loop merging all the files from consecutive parms which relate to the
462 same directory. Note issuing a directory header with no contents
463 mirrors what windows does */
465 fd
= HeapAlloc(GetProcessHeap(),0,sizeof(WIN32_FIND_DATA
));
466 while (parms
&& strcmp(inputparms
->dirName
, parms
->dirName
) == 0) {
469 /* Work out the full path + filename */
470 strcpy(real_path
, parms
->dirName
);
471 strcat(real_path
, parms
->fileName
);
473 /* Load all files into an in memory structure */
474 WINE_TRACE("Looking for matches to '%s'\n", real_path
);
475 hff
= FindFirstFile (real_path
, (fd
+entry_count
));
476 if (hff
!= INVALID_HANDLE_VALUE
) {
478 /* Skip any which are filtered out by attribute */
479 if (((fd
+entry_count
)->dwFileAttributes
& attrsbits
) != showattrs
) continue;
483 /* Keep running track of longest filename for wide output */
484 if (wide
|| orderByCol
) {
485 int tmpLen
= strlen((fd
+(entry_count
-1))->cFileName
) + 3;
486 if ((fd
+(entry_count
-1))->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) tmpLen
= tmpLen
+ 2;
487 if (tmpLen
> widest
) widest
= tmpLen
;
490 fd
= HeapReAlloc(GetProcessHeap(),0,fd
,(entry_count
+1)*sizeof(WIN32_FIND_DATA
));
493 WCMD_output ("Memory Allocation Error");
497 } while (FindNextFile(hff
, (fd
+entry_count
)) != 0);
501 /* Work out the actual current directory name without a trailing \ */
502 strcpy(real_path
, parms
->dirName
);
503 real_path
[strlen(parms
->dirName
)-1] = 0x00;
505 /* Output the results */
507 if (level
!= 0 && (entry_count
> 0)) WCMD_output ("\n");
508 if (!recurse
|| ((entry_count
> 0) && done_header
==FALSE
)) {
509 WCMD_output ("Directory of %s\n\n", real_path
);
514 /* Move to next parm */
518 /* Handle case where everything is filtered out */
519 if (entry_count
> 0) {
521 /* Sort the list of files */
522 qsort (fd
, entry_count
, sizeof(WIN32_FIND_DATA
), WCMD_dir_sort
);
524 /* Work out the number of columns */
525 WINE_TRACE("%d entries, maxwidth=%d, widest=%d\n", entry_count
, max_width
, widest
);
526 if (wide
|| orderByCol
) {
527 numCols
= max(1, (int)max_width
/ widest
);
528 numRows
= entry_count
/ numCols
;
529 if (entry_count
% numCols
) numRows
++;
532 numRows
= entry_count
;
534 WINE_TRACE("cols=%d, rows=%d\n", numCols
, numRows
);
536 for (rows
=0; rows
<numRows
; rows
++) {
537 BOOL addNewLine
= TRUE
;
538 for (cols
=0; cols
<numCols
; cols
++) {
541 /* Work out the index of the entry being pointed to */
543 i
= (cols
* numRows
) + rows
;
544 if (i
>= entry_count
) continue;
546 i
= (rows
* numCols
) + cols
;
547 if (i
>= entry_count
) continue;
550 /* /L convers all names to lower case */
552 char *p
= (fd
+i
)->cFileName
;
553 while ( (*p
= tolower(*p
)) ) ++p
;
556 /* /Q gets file ownership information */
558 lstrcpy (string
, inputparms
->dirName
);
559 lstrcat (string
, (fd
+i
)->cFileName
);
560 WCMD_getfileowner(string
, username
, sizeof(username
));
563 if (dirTime
== Written
) {
564 FileTimeToLocalFileTime (&(fd
+i
)->ftLastWriteTime
, &ft
);
565 } else if (dirTime
== Access
) {
566 FileTimeToLocalFileTime (&(fd
+i
)->ftLastAccessTime
, &ft
);
568 FileTimeToLocalFileTime (&(fd
+i
)->ftCreationTime
, &ft
);
570 FileTimeToSystemTime (&ft
, &st
);
571 GetDateFormat (0, DATE_SHORTDATE
, &st
, NULL
, datestring
,
573 GetTimeFormat (0, TIME_NOSECONDS
, &st
,
574 NULL
, timestring
, sizeof(timestring
));
578 tmp_width
= cur_width
;
579 if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
580 WCMD_output ("[%s]", (fd
+i
)->cFileName
);
582 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) + 2;
584 WCMD_output ("%s", (fd
+i
)->cFileName
);
585 tmp_width
= tmp_width
+ strlen((fd
+i
)->cFileName
) ;
587 file_size
.u
.LowPart
= (fd
+i
)->nFileSizeLow
;
588 file_size
.u
.HighPart
= (fd
+i
)->nFileSizeHigh
;
589 byte_count
.QuadPart
+= file_size
.QuadPart
;
591 cur_width
= cur_width
+ widest
;
593 if ((cur_width
+ widest
) > max_width
) {
596 WCMD_output ("%*.s", (tmp_width
- cur_width
) ,"");
599 } else if ((fd
+i
)->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
603 WCMD_output ("%10s %8s <DIR> ", datestring
, timestring
);
604 if (shortname
) WCMD_output ("%-13s", (fd
+i
)->cAlternateFileName
);
605 if (usernames
) WCMD_output ("%-23s", username
);
606 WCMD_output("%s",(fd
+i
)->cFileName
);
608 if (!((strcmp((fd
+i
)->cFileName
, ".") == 0) ||
609 (strcmp((fd
+i
)->cFileName
, "..") == 0))) {
610 WCMD_output ("%s%s", recurse
?inputparms
->dirName
:"", (fd
+i
)->cFileName
);
618 file_size
.u
.LowPart
= (fd
+i
)->nFileSizeLow
;
619 file_size
.u
.HighPart
= (fd
+i
)->nFileSizeHigh
;
620 byte_count
.QuadPart
+= file_size
.QuadPart
;
622 WCMD_output ("%10s %8s %10s ", datestring
, timestring
,
623 WCMD_filesize64(file_size
.QuadPart
));
624 if (shortname
) WCMD_output ("%-13s", (fd
+i
)->cAlternateFileName
);
625 if (usernames
) WCMD_output ("%-23s", username
);
626 WCMD_output("%s",(fd
+i
)->cFileName
);
628 WCMD_output ("%s%s", recurse
?inputparms
->dirName
:"", (fd
+i
)->cFileName
);
632 if (addNewLine
) WCMD_output ("\n");
637 if (file_count
== 1) {
638 WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count
.QuadPart
));
641 WCMD_output ("%8d files %24s bytes\n", file_count
, WCMD_filesize64 (byte_count
.QuadPart
));
644 byte_total
= byte_total
+ byte_count
.QuadPart
;
645 file_total
= file_total
+ file_count
;
646 dir_total
= dir_total
+ dir_count
;
648 if (!bare
&& !recurse
) {
649 if (dir_count
== 1) WCMD_output ("%8d directory ", 1);
650 else WCMD_output ("%8d directories", dir_count
);
653 HeapFree(GetProcessHeap(),0,fd
);
655 /* When recursing, look in all subdirectories for matches */
657 DIRECTORY_STACK
*dirStack
= NULL
;
658 DIRECTORY_STACK
*lastEntry
= NULL
;
659 WIN32_FIND_DATA finddata
;
661 /* Build path to search */
662 strcpy(string
, inputparms
->dirName
);
665 WINE_TRACE("Recursive, looking for '%s'\n", string
);
666 hff
= FindFirstFile (string
, &finddata
);
667 if (hff
!= INVALID_HANDLE_VALUE
) {
669 if ((finddata
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) &&
670 (strcmp(finddata
.cFileName
, "..") != 0) &&
671 (strcmp(finddata
.cFileName
, ".") != 0)) {
673 DIRECTORY_STACK
*thisDir
;
674 int dirsToCopy
= concurrentDirs
;
676 /* Loop creating list of subdirs for all concurrent entries */
678 while (dirsToCopy
> 0) {
681 /* Work out search parameter in sub dir */
682 strcpy (string
, inputparms
->dirName
);
683 strcat (string
, finddata
.cFileName
);
684 strcat (string
, "\\");
685 WINE_TRACE("Recursive, Adding to search list '%s'\n", string
);
687 /* Allocate memory, add to list */
688 thisDir
= (DIRECTORY_STACK
*) HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK
));
689 if (dirStack
== NULL
) dirStack
= thisDir
;
690 if (lastEntry
!= NULL
) lastEntry
->next
= thisDir
;
692 thisDir
->next
= NULL
;
693 thisDir
->dirName
= HeapAlloc(GetProcessHeap(),0,(strlen(string
)+1));
694 strcpy(thisDir
->dirName
, string
);
695 thisDir
->fileName
= HeapAlloc(GetProcessHeap(),0,(strlen(parms
->fileName
)+1));
696 strcpy(thisDir
->fileName
, parms
->fileName
);
700 } while (FindNextFile(hff
, &finddata
) != 0);
703 while (dirStack
!= NULL
) {
704 DIRECTORY_STACK
*thisDir
= dirStack
;
705 dirStack
= WCMD_list_directory (thisDir
, 1);
706 while (thisDir
!= dirStack
) {
707 DIRECTORY_STACK
*tempDir
= thisDir
->next
;
708 HeapFree(GetProcessHeap(),0,thisDir
->dirName
);
709 HeapFree(GetProcessHeap(),0,thisDir
->fileName
);
710 HeapFree(GetProcessHeap(),0,thisDir
);
717 /* Handle case where everything is filtered out */
718 if ((file_total
+ dir_total
== 0) && (level
== 0)) {
719 SetLastError (ERROR_FILE_NOT_FOUND
);
727 /*****************************************************************************
730 * Convert a 64-bit number into a character string, with commas every three digits.
731 * Result is returned in a static string overwritten with each call.
732 * FIXME: There must be a better algorithm!
735 char * WCMD_filesize64 (ULONGLONG n
) {
740 static char buff
[32];
745 if (separator
&& ((++i
)%3 == 1)) *p
++ = ',';
756 /*****************************************************************************
759 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
762 char * WCMD_strrev (char *buff
) {
768 for (i
=0; i
<r
/2; i
++) {
770 buff
[i
] = buff
[r
-i
-1];
777 /*****************************************************************************
780 * Sort based on the /O options supplied on the command line
782 int WCMD_dir_sort (const void *a
, const void *b
)
784 WIN32_FIND_DATA
*filea
= (WIN32_FIND_DATA
*)a
;
785 WIN32_FIND_DATA
*fileb
= (WIN32_FIND_DATA
*)b
;
788 /* If /OG or /O-G supplied, dirs go at the top or bottom, ignoring the
789 requested sort order for the directory components */
790 if (orderGroupDirs
&&
791 ((filea
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) ||
792 (fileb
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)))
794 BOOL aDir
= filea
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
795 if (aDir
) result
= -1;
797 if (orderGroupDirsReverse
) result
= -result
;
801 } else if (dirOrder
== Name
) {
802 result
= lstrcmpi(filea
->cFileName
, fileb
->cFileName
);
805 } else if (dirOrder
== Size
) {
806 ULONG64 sizea
= (((ULONG64
)filea
->nFileSizeHigh
) << 32) + filea
->nFileSizeLow
;
807 ULONG64 sizeb
= (((ULONG64
)fileb
->nFileSizeHigh
) << 32) + fileb
->nFileSizeLow
;
808 if( sizea
< sizeb
) result
= -1;
809 else if( sizea
== sizeb
) result
= 0;
812 /* Order by Date: (Takes into account which date (/T option) */
813 } else if (dirOrder
== Date
) {
816 ULONG64 timea
, timeb
;
818 if (dirTime
== Written
) {
819 ft
= &filea
->ftLastWriteTime
;
820 timea
= (((ULONG64
)ft
->dwHighDateTime
) << 32) + ft
->dwLowDateTime
;
821 ft
= &fileb
->ftLastWriteTime
;
822 timeb
= (((ULONG64
)ft
->dwHighDateTime
) << 32) + ft
->dwLowDateTime
;
823 } else if (dirTime
== Access
) {
824 ft
= &filea
->ftLastAccessTime
;
825 timea
= (((ULONG64
)ft
->dwHighDateTime
) << 32) + ft
->dwLowDateTime
;
826 ft
= &fileb
->ftLastAccessTime
;
827 timeb
= (((ULONG64
)ft
->dwHighDateTime
) << 32) + ft
->dwLowDateTime
;
829 ft
= &filea
->ftCreationTime
;
830 timea
= (((ULONG64
)ft
->dwHighDateTime
) << 32) + ft
->dwLowDateTime
;
831 ft
= &fileb
->ftCreationTime
;
832 timeb
= (((ULONG64
)ft
->dwHighDateTime
) << 32) + ft
->dwLowDateTime
;
834 if( timea
< timeb
) result
= -1;
835 else if( timea
== timeb
) result
= 0;
838 /* Order by Extension: (Takes into account which date (/T option) */
839 } else if (dirOrder
== Extension
) {
842 char fname
[MAX_PATH
];
846 /* Split into components */
847 WCMD_splitpath(filea
->cFileName
, drive
, dir
, fname
, extA
);
848 WCMD_splitpath(fileb
->cFileName
, drive
, dir
, fname
, extB
);
849 result
= lstrcmpi(extA
, extB
);
852 if (orderReverse
) result
= -result
;
856 /*****************************************************************************
859 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
861 void WCMD_getfileowner(char *filename
, char *owner
, int ownerlen
) {
863 ULONG sizeNeeded
= 0;
865 char name
[MAXSTRING
];
866 char domain
[MAXSTRING
];
868 /* In case of error, return empty string */
871 /* Find out how much space we need for the owner security descritpor */
872 GetFileSecurity(filename
, OWNER_SECURITY_INFORMATION
, 0, 0, &sizeNeeded
);
875 if(rc
== ERROR_INSUFFICIENT_BUFFER
&& sizeNeeded
> 0) {
879 BOOL defaulted
= FALSE
;
880 ULONG nameLen
= MAXSTRING
;
881 ULONG domainLen
= MAXSTRING
;
882 SID_NAME_USE nameuse
;
884 secBuffer
= (LPBYTE
) HeapAlloc(GetProcessHeap(),0,sizeNeeded
* sizeof(BYTE
));
885 if(!secBuffer
) return;
887 /* Get the owners security descriptor */
888 if(!GetFileSecurity(filename
, OWNER_SECURITY_INFORMATION
, secBuffer
,
889 sizeNeeded
, &sizeNeeded
)) {
890 HeapFree(GetProcessHeap(),0,secBuffer
);
894 /* Get the SID from the SD */
895 if(!GetSecurityDescriptorOwner(secBuffer
, &pSID
, &defaulted
)) {
896 HeapFree(GetProcessHeap(),0,secBuffer
);
900 /* Convert to a username */
901 if (LookupAccountSid(NULL
, pSID
, name
, &nameLen
, domain
, &domainLen
, &nameuse
)) {
902 snprintf(owner
, ownerlen
, "%s%c%s", domain
, '\\', name
);
904 HeapFree(GetProcessHeap(),0,secBuffer
);
909 /*****************************************************************************
912 * Print out the trailer for the supplied drive letter
914 static void WCMD_dir_trailer(char drive
) {
915 ULARGE_INTEGER avail
, total
, freebytes
;
917 char driveName
[4] = "c:\\";
919 driveName
[0] = drive
;
920 status
= GetDiskFreeSpaceEx (driveName
, &avail
, &total
, &freebytes
);
921 WINE_TRACE("Writing trailer for '%s' gave %d(%d)\n", driveName
, status
, GetLastError());
923 if (errorlevel
==0 && !bare
) {
925 WCMD_output ("\n Total files listed:\n%8d files%25s bytes\n",
926 file_total
, WCMD_filesize64 (byte_total
));
927 WCMD_output ("%8d directories %18s bytes free\n\n",
928 dir_total
, WCMD_filesize64 (freebytes
.QuadPart
));
930 WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (freebytes
.QuadPart
));