cmd.exe: Add support for dir /A filtering.
[wine/multimedia.git] / programs / cmd / directory.c
blob0f514e93ef8afe40e514ce61086d24ade36313bd
1 /*
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
22 * NOTES:
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
30 #include "wcmd.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(cmd);
35 int WCMD_dir_sort (const void *a, const void *b);
36 void WCMD_list_directory (char *path, int level);
37 char * WCMD_filesize64 (ULONGLONG free);
38 char * WCMD_strrev (char *buff);
39 static void WCMD_getfileowner(char *filename, char *owner, int ownerlen);
41 extern int echo_mode;
42 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
43 extern DWORD errorlevel;
45 typedef enum _DISPLAYTIME
47 Creation = 0,
48 Access,
49 Written
50 } DISPLAYTIME;
52 typedef enum _DISPLAYORDER
54 Name = 0,
55 Extension,
56 Size,
57 Date
58 } DISPLAYORDER;
60 static int file_total, dir_total, recurse, wide, bare, max_width, lower;
61 static int shortname, usernames;
62 static ULONGLONG byte_total;
63 static DISPLAYTIME dirTime;
64 static DISPLAYORDER dirOrder;
65 static BOOL orderReverse, orderGroupDirs, orderGroupDirsReverse, orderByCol;
66 static BOOL seperator;
67 static ULONG showattrs, attrsbits;
69 /*****************************************************************************
70 * WCMD_directory
72 * List a file directory.
76 void WCMD_directory (void) {
78 char path[MAX_PATH], drive[8];
79 int status, paged_mode;
80 ULARGE_INTEGER avail, total, free;
81 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
82 char *p;
83 char string[MAXSTRING];
85 /* Prefill Quals with (uppercased) DIRCMD env var */
86 if (GetEnvironmentVariable ("DIRCMD", string, sizeof(string))) {
87 p = string;
88 while ( (*p = toupper(*p)) ) ++p;
89 strcat(string,quals);
90 strcpy(quals, string);
93 byte_total = 0;
94 file_total = dir_total = 0;
96 /* Initialize all flags to their defaults as if no DIRCMD or quals */
97 paged_mode = FALSE;
98 recurse = FALSE;
99 wide = FALSE;
100 bare = FALSE;
101 lower = FALSE;
102 shortname = FALSE;
103 usernames = FALSE;
104 orderByCol = FALSE;
105 seperator = TRUE;
106 dirTime = Written;
107 dirOrder = Name;
108 orderReverse = FALSE;
109 orderGroupDirs = FALSE;
110 orderGroupDirsReverse = FALSE;
111 showattrs = 0;
112 attrsbits = 0;
114 /* Handle args - Loop through so right most is the effective one */
115 /* Note: /- appears to be a negate rather than an off, eg. dir
116 /-W is wide, or dir /w /-w /-w is also wide */
117 p = quals;
118 while (*p && (*p=='/' || *p==' ')) {
119 BOOL negate = FALSE;
120 if (*p++==' ') continue; /* Skip / and blanks introduced through DIRCMD */
122 if (*p=='-') {
123 negate = TRUE;
124 p++;
127 WINE_TRACE("Processing arg '%c' (in %s)\n", *p, quals);
128 switch (*p) {
129 case 'P': if (negate) paged_mode = !paged_mode;
130 else paged_mode = TRUE;
131 break;
132 case 'S': if (negate) recurse = !recurse;
133 else recurse = TRUE;
134 break;
135 case 'W': if (negate) wide = !wide;
136 else wide = TRUE;
137 break;
138 case 'B': if (negate) bare = !bare;
139 else bare = TRUE;
140 break;
141 case 'L': if (negate) lower = !lower;
142 else lower = TRUE;
143 break;
144 case 'X': if (negate) shortname = !shortname;
145 else shortname = TRUE;
146 break;
147 case 'Q': if (negate) usernames = !usernames;
148 else usernames = TRUE;
149 break;
150 case 'D': if (negate) orderByCol = !orderByCol;
151 else orderByCol = TRUE;
152 break;
153 case 'C': if (negate) seperator = !seperator;
154 else seperator = TRUE;
155 break;
156 case 'T': p = p + 1;
157 if (*p==':') p++; /* Skip optional : */
159 if (*p == 'A') dirTime = Access;
160 else if (*p == 'C') dirTime = Creation;
161 else if (*p == 'W') dirTime = Written;
163 /* Support /T and /T: with no parms, default to written */
164 else if (*p == 0x00 || *p == '/') {
165 dirTime = Written;
166 p = p - 1; /* So when step on, move to '/' */
167 } else {
168 SetLastError(ERROR_INVALID_PARAMETER);
169 WCMD_print_error();
170 return;
172 break;
173 case 'O': p = p + 1;
174 if (*p==':') p++; /* Skip optional : */
175 while (*p && *p != '/') {
176 WINE_TRACE("Processing subparm '%c' (in %s)\n", *p, quals);
177 switch (*p) {
178 case 'N': dirOrder = Name; break;
179 case 'E': dirOrder = Extension; break;
180 case 'S': dirOrder = Size; break;
181 case 'D': dirOrder = Date; break;
182 case '-': if (*(p+1)=='G') orderGroupDirsReverse=TRUE;
183 else orderReverse = TRUE;
184 break;
185 case 'G': orderGroupDirs = TRUE; break;
186 default:
187 SetLastError(ERROR_INVALID_PARAMETER);
188 WCMD_print_error();
189 return;
191 p++;
193 p = p - 1; /* So when step on, move to '/' */
194 break;
195 case 'A': p = p + 1;
196 showattrs = 0;
197 attrsbits = 0;
198 if (*p==':') p++; /* Skip optional : */
199 while (*p && *p != '/') {
200 BOOL anegate = FALSE;
201 ULONG mask;
203 /* Note /A: - options are 'offs' not toggles */
204 if (*p=='-') {
205 anegate = TRUE;
206 p++;
209 WINE_TRACE("Processing subparm '%c' (in %s)\n", *p, quals);
210 switch (*p) {
211 case 'D': mask = FILE_ATTRIBUTE_DIRECTORY; break;
212 case 'H': mask = FILE_ATTRIBUTE_HIDDEN; break;
213 case 'S': mask = FILE_ATTRIBUTE_SYSTEM; break;
214 case 'R': mask = FILE_ATTRIBUTE_READONLY; break;
215 case 'A': mask = FILE_ATTRIBUTE_ARCHIVE; break;
216 default:
217 SetLastError(ERROR_INVALID_PARAMETER);
218 WCMD_print_error();
219 return;
222 /* Keep running list of bits we care about */
223 attrsbits |= mask;
225 /* Mask shows what MUST be in the bits we care about */
226 if (anegate) showattrs = showattrs & ~mask;
227 else showattrs |= mask;
229 p++;
231 p = p - 1; /* So when step on, move to '/' */
232 WINE_TRACE("Result: showattrs %x, bits %x\n", showattrs, attrsbits);
233 break;
234 default:
235 SetLastError(ERROR_INVALID_PARAMETER);
236 WCMD_print_error();
237 return;
239 p = p + 1;
242 /* Handle conflicting args and initialization */
243 if (bare || shortname) wide = FALSE;
244 if (bare) shortname = FALSE;
245 if (wide) usernames = FALSE;
246 if (orderByCol) wide = TRUE;
248 if (wide) {
249 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo))
250 max_width = consoleInfo.dwSize.X;
251 else
252 max_width = 80;
254 if (paged_mode) {
255 WCMD_enter_paged_mode();
258 if (param1[0] == '\0') strcpy (param1, ".");
259 status = GetFullPathName (param1, sizeof(path), path, NULL);
260 if (!status) {
261 WCMD_print_error();
262 if (paged_mode) WCMD_leave_paged_mode();
263 return;
265 lstrcpyn (drive, path, 3);
267 if (!bare) {
268 status = WCMD_volume (0, drive);
269 if (!status) {
270 if (paged_mode) WCMD_leave_paged_mode();
271 return;
275 WCMD_list_directory (path, 0);
276 lstrcpyn (drive, path, 4);
277 GetDiskFreeSpaceEx (drive, &avail, &total, &free);
279 if (!bare) {
280 if (recurse) {
281 WCMD_output ("\n\n Total files listed:\n%8d files%25s bytes\n",
282 file_total, WCMD_filesize64 (byte_total));
283 WCMD_output ("%8d directories %18s bytes free\n\n",
284 dir_total, WCMD_filesize64 (free.QuadPart));
285 } else {
286 WCMD_output (" %18s bytes free\n\n", WCMD_filesize64 (free.QuadPart));
289 if (paged_mode) WCMD_leave_paged_mode();
292 /*****************************************************************************
293 * WCMD_list_directory
295 * List a single file directory. This function (and those below it) can be called
296 * recursively when the /S switch is used.
298 * FIXME: Entries sorted by name only. Should we support DIRCMD??
299 * FIXME: Assumes 24-line display for the /P qualifier.
300 * FIXME: Other command qualifiers not supported.
301 * FIXME: DIR /S FILENAME fails if at least one matching file is not found in the top level.
304 void WCMD_list_directory (char *search_path, int level) {
306 char string[1024], datestring[32], timestring[32];
307 char *p;
308 char real_path[MAX_PATH];
309 WIN32_FIND_DATA *fd;
310 FILETIME ft;
311 SYSTEMTIME st;
312 HANDLE hff;
313 int status, dir_count, file_count, entry_count, i, widest, cur_width, tmp_width;
314 int numCols, numRows;
315 int rows, cols;
316 ULARGE_INTEGER byte_count, file_size;
318 dir_count = 0;
319 file_count = 0;
320 entry_count = 0;
321 byte_count.QuadPart = 0;
322 widest = 0;
323 cur_width = 0;
326 * If the path supplied does not include a wildcard, and the endpoint of the
327 * path references a directory, we need to list the *contents* of that
328 * directory not the directory file itself.
331 if ((strchr(search_path, '*') == NULL) && (strchr(search_path, '%') == NULL)) {
332 status = GetFileAttributes (search_path);
333 if ((status != INVALID_FILE_ATTRIBUTES) && (status & FILE_ATTRIBUTE_DIRECTORY)) {
334 if (search_path[strlen(search_path)-1] == '\\') {
335 strcat (search_path, "*");
337 else {
338 strcat (search_path, "\\*");
343 /* Work out the actual current directory name */
344 p = strrchr (search_path, '\\');
345 memset(real_path, 0x00, sizeof(real_path));
346 lstrcpyn (real_path, search_path, (p-search_path+2));
348 /* Load all files into an in memory structure */
349 fd = HeapAlloc(GetProcessHeap(),0,sizeof(WIN32_FIND_DATA));
350 hff = FindFirstFile (search_path, fd);
351 if (hff == INVALID_HANDLE_VALUE) {
352 SetLastError (ERROR_FILE_NOT_FOUND);
353 WCMD_print_error ();
354 HeapFree(GetProcessHeap(),0,fd);
355 return;
357 do {
358 /* Skip any which are filtered out by attribute */
359 if (((fd+entry_count)->dwFileAttributes & attrsbits) != showattrs) continue;
361 entry_count++;
363 /* Keep running track of longest filename for wide output */
364 if (wide || orderByCol) {
365 int tmpLen = strlen((fd+(entry_count-1))->cFileName) + 3;
366 if ((fd+(entry_count-1))->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) tmpLen = tmpLen + 2;
367 if (tmpLen > widest) widest = tmpLen;
370 fd = HeapReAlloc(GetProcessHeap(),0,fd,(entry_count+1)*sizeof(WIN32_FIND_DATA));
371 if (fd == NULL) {
372 FindClose (hff);
373 WCMD_output ("Memory Allocation Error");
374 return;
376 } while (FindNextFile(hff, (fd+entry_count)) != 0);
377 FindClose (hff);
379 /* Handle case where everything is filtered out */
380 if (entry_count == 0) {
381 SetLastError (ERROR_FILE_NOT_FOUND);
382 WCMD_print_error ();
383 HeapFree(GetProcessHeap(),0,fd);
384 return;
387 /* Sort the list of files */
388 qsort (fd, entry_count, sizeof(WIN32_FIND_DATA), WCMD_dir_sort);
390 /* Output the results */
391 if (!bare) {
392 if (level != 0) WCMD_output ("\n\n");
393 WCMD_output ("Directory of %s\n\n", real_path);
396 /* Work out the number of columns */
397 WINE_TRACE("%d entries, maxwidth=%d, widest=%d\n", entry_count, max_width, widest);
398 if (wide || orderByCol) {
399 numCols = max(1, (int)max_width / widest);
400 numRows = entry_count / numCols;
401 if (entry_count % numCols) numRows++;
402 } else {
403 numCols = 1;
404 numRows = entry_count;
406 WINE_TRACE("cols=%d, rows=%d\n", numCols, numRows);
408 for (rows=0; rows<numRows; rows++) {
409 for (cols=0; cols<numCols; cols++) {
410 char username[24];
412 /* Work out the index of the entry being pointed to */
413 if (orderByCol) {
414 i = (cols * numRows) + rows;
415 if (i >= entry_count) continue;
416 } else {
417 i = (rows * numCols) + cols;
418 if (i >= entry_count) continue;
421 /* /L convers all names to lower case */
422 if (lower) {
423 char *p = (fd+i)->cFileName;
424 while ( (*p = tolower(*p)) ) ++p;
427 /* /Q gets file ownership information */
428 if (usernames) {
429 p = strrchr (search_path, '\\');
430 lstrcpyn (string, search_path, (p-search_path+2));
431 lstrcat (string, (fd+i)->cFileName);
432 WCMD_getfileowner(string, username, sizeof(username));
435 if (dirTime == Written) {
436 FileTimeToLocalFileTime (&(fd+i)->ftLastWriteTime, &ft);
437 } else if (dirTime == Access) {
438 FileTimeToLocalFileTime (&(fd+i)->ftLastAccessTime, &ft);
439 } else {
440 FileTimeToLocalFileTime (&(fd+i)->ftCreationTime, &ft);
442 FileTimeToSystemTime (&ft, &st);
443 GetDateFormat (0, DATE_SHORTDATE, &st, NULL, datestring,
444 sizeof(datestring));
445 GetTimeFormat (0, TIME_NOSECONDS, &st,
446 NULL, timestring, sizeof(timestring));
448 if (wide) {
450 tmp_width = cur_width;
451 if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
452 WCMD_output ("[%s]", (fd+i)->cFileName);
453 dir_count++;
454 tmp_width = tmp_width + strlen((fd+i)->cFileName) + 2;
455 } else {
456 WCMD_output ("%s", (fd+i)->cFileName);
457 tmp_width = tmp_width + strlen((fd+i)->cFileName) ;
458 file_count++;
459 file_size.u.LowPart = (fd+i)->nFileSizeLow;
460 file_size.u.HighPart = (fd+i)->nFileSizeHigh;
461 byte_count.QuadPart += file_size.QuadPart;
463 cur_width = cur_width + widest;
465 if ((cur_width + widest) > max_width) {
466 cur_width = 0;
467 } else {
468 WCMD_output ("%*.s", (tmp_width - cur_width) ,"");
471 } else if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
472 dir_count++;
474 if (!bare) {
475 WCMD_output ("%10s %8s <DIR> ", datestring, timestring);
476 if (shortname) WCMD_output ("%-13s", (fd+i)->cAlternateFileName);
477 if (usernames) WCMD_output ("%-23s", username);
478 WCMD_output("%s",(fd+i)->cFileName);
479 } else {
480 if (!((strcmp((fd+i)->cFileName, ".") == 0) ||
481 (strcmp((fd+i)->cFileName, "..") == 0))) {
482 WCMD_output ("%s%s", recurse?real_path:"", (fd+i)->cFileName);
486 else {
487 file_count++;
488 file_size.u.LowPart = (fd+i)->nFileSizeLow;
489 file_size.u.HighPart = (fd+i)->nFileSizeHigh;
490 byte_count.QuadPart += file_size.QuadPart;
491 if (!bare) {
492 WCMD_output ("%10s %8s %10s ", datestring, timestring,
493 WCMD_filesize64(file_size.QuadPart));
494 if (shortname) WCMD_output ("%-13s", (fd+i)->cAlternateFileName);
495 if (usernames) WCMD_output ("%-23s", username);
496 WCMD_output("%s",(fd+i)->cFileName);
497 } else {
498 WCMD_output ("%s%s", recurse?real_path:"", (fd+i)->cFileName);
502 WCMD_output ("\n");
503 cur_width = 0;
506 if (!bare) {
507 if (file_count == 1) {
508 WCMD_output (" 1 file %25s bytes\n", WCMD_filesize64 (byte_count.QuadPart));
510 else {
511 WCMD_output ("%8d files %24s bytes\n", file_count, WCMD_filesize64 (byte_count.QuadPart));
514 byte_total = byte_total + byte_count.QuadPart;
515 file_total = file_total + file_count;
516 dir_total = dir_total + dir_count;
518 if (!bare) {
519 if (dir_count == 1) WCMD_output ("1 directory ");
520 else WCMD_output ("%8d directories", dir_count);
522 for (i=0; i<entry_count; i++) {
523 if ((recurse) &&
524 ((fd+i)->cFileName[0] != '.') &&
525 ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
526 #if 0
527 GetFullPathName ((fd+i)->cFileName, sizeof(string), string, NULL);
528 #endif
529 p = strrchr (search_path, '\\');
530 lstrcpyn (string, search_path, (p-search_path+2));
531 lstrcat (string, (fd+i)->cFileName);
532 lstrcat (string, p);
533 WCMD_list_directory (string, 1);
536 HeapFree(GetProcessHeap(),0,fd);
537 return;
540 /*****************************************************************************
541 * WCMD_filesize64
543 * Convert a 64-bit number into a character string, with commas every three digits.
544 * Result is returned in a static string overwritten with each call.
545 * FIXME: There must be a better algorithm!
548 char * WCMD_filesize64 (ULONGLONG n) {
550 ULONGLONG q;
551 unsigned int r, i;
552 char *p;
553 static char buff[32];
555 p = buff;
556 i = -3;
557 do {
558 if (seperator && ((++i)%3 == 1)) *p++ = ',';
559 q = n / 10;
560 r = n - (q * 10);
561 *p++ = r + '0';
562 *p = '\0';
563 n = q;
564 } while (n != 0);
565 WCMD_strrev (buff);
566 return buff;
569 /*****************************************************************************
570 * WCMD_strrev
572 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
575 char * WCMD_strrev (char *buff) {
577 int r, i;
578 char b;
580 r = lstrlen (buff);
581 for (i=0; i<r/2; i++) {
582 b = buff[i];
583 buff[i] = buff[r-i-1];
584 buff[r-i-1] = b;
586 return (buff);
590 /*****************************************************************************
591 * WCMD_dir_sort
593 * Sort based on the /O options supplied on the command line
595 int WCMD_dir_sort (const void *a, const void *b)
597 WIN32_FIND_DATA *filea = (WIN32_FIND_DATA *)a;
598 WIN32_FIND_DATA *fileb = (WIN32_FIND_DATA *)b;
599 int result = 0;
601 /* If /OG or /O-G supplied, dirs go at the top or bottom, ignoring the
602 requested sort order for the directory components */
603 if (orderGroupDirs &&
604 ((filea->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
605 (fileb->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
607 BOOL aDir = filea->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
608 if (aDir) result = -1;
609 else result = 1;
610 if (orderGroupDirsReverse) result = -result;
611 return result;
613 /* Order by Name: */
614 } else if (dirOrder == Name) {
615 result = lstrcmpi(filea->cFileName, fileb->cFileName);
617 /* Order by Size: */
618 } else if (dirOrder == Size) {
619 ULONG64 sizea = (((ULONG64)filea->nFileSizeHigh) << 32) + filea->nFileSizeLow;
620 ULONG64 sizeb = (((ULONG64)fileb->nFileSizeHigh) << 32) + fileb->nFileSizeLow;
621 if( sizea < sizeb ) result = -1;
622 else if( sizea == sizeb ) result = 0;
623 else result = 1;
625 /* Order by Date: (Takes into account which date (/T option) */
626 } else if (dirOrder == Date) {
628 FILETIME *ft;
629 ULONG64 timea, timeb;
631 if (dirTime == Written) {
632 ft = &filea->ftLastWriteTime;
633 timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
634 ft = &fileb->ftLastWriteTime;
635 timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
636 } else if (dirTime == Access) {
637 ft = &filea->ftLastAccessTime;
638 timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
639 ft = &fileb->ftLastAccessTime;
640 timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
641 } else {
642 ft = &filea->ftCreationTime;
643 timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
644 ft = &fileb->ftCreationTime;
645 timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
647 if( timea < timeb ) result = -1;
648 else if( timea == timeb ) result = 0;
649 else result = 1;
651 /* Order by Extension: (Takes into account which date (/T option) */
652 } else if (dirOrder == Extension) {
653 char drive[10];
654 char dir[MAX_PATH];
655 char fname[MAX_PATH];
656 char extA[MAX_PATH];
657 char extB[MAX_PATH];
659 /* Split into components */
660 WCMD_splitpath(filea->cFileName, drive, dir, fname, extA);
661 WCMD_splitpath(fileb->cFileName, drive, dir, fname, extB);
662 result = lstrcmpi(extA, extB);
665 if (orderReverse) result = -result;
666 return result;
669 /*****************************************************************************
670 * WCMD_getfileowner
672 * Reverse a character string in-place (strrev() is not available under unixen :-( ).
674 void WCMD_getfileowner(char *filename, char *owner, int ownerlen) {
676 ULONG sizeNeeded = 0;
677 DWORD rc;
678 char name[MAXSTRING];
679 char domain[MAXSTRING];
681 /* In case of error, return empty string */
682 *owner = 0x00;
684 /* Find out how much space we need for the owner security descritpor */
685 GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, 0, 0, &sizeNeeded);
686 rc = GetLastError();
688 if(rc == ERROR_INSUFFICIENT_BUFFER && sizeNeeded > 0) {
690 LPBYTE secBuffer;
691 PSID pSID = NULL;
692 BOOL defaulted = FALSE;
693 ULONG nameLen = MAXSTRING;
694 ULONG domainLen = MAXSTRING;
695 SID_NAME_USE nameuse;
697 secBuffer = (LPBYTE) HeapAlloc(GetProcessHeap(),0,sizeNeeded * sizeof(BYTE));
698 if(!secBuffer) return;
700 /* Get the owners security descriptor */
701 if(!GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, secBuffer,
702 sizeNeeded, &sizeNeeded)) {
703 HeapFree(GetProcessHeap(),0,secBuffer);
704 return;
707 /* Get the SID from the SD */
708 if(!GetSecurityDescriptorOwner(secBuffer, &pSID, &defaulted)) {
709 HeapFree(GetProcessHeap(),0,secBuffer);
710 return;
713 /* Convert to a username */
714 if (LookupAccountSid(NULL, pSID, name, &nameLen, domain, &domainLen, &nameuse)) {
715 snprintf(owner, ownerlen, "%s%c%s", domain, '\\', name);
717 HeapFree(GetProcessHeap(),0,secBuffer);
719 return;