diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c index c70fca5304b..cd17d31d719 100644 --- a/programs/cmd/directory.c +++ b/programs/cmd/directory.c @@ -33,11 +33,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(cmd); -int WCMD_dir_sort (const void *a, const void *b); -WCHAR * WCMD_filesize64 (ULONGLONG free); -WCHAR * WCMD_strrev (WCHAR *buff); -static void WCMD_getfileowner(WCHAR *filename, WCHAR *owner, int ownerlen); -static void WCMD_dir_trailer(WCHAR drive); extern int echo_mode; extern WCHAR quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH]; @@ -58,7 +53,6 @@ typedef enum _DISPLAYORDER Date } DISPLAYORDER; -static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *parms, int level); static int file_total, dir_total, recurse, wide, bare, max_width, lower; static int shortname, usernames; static ULONGLONG byte_total; @@ -75,6 +69,560 @@ static const WCHAR slashW[] = {'\\','\0'}; static const WCHAR emptyW[] = {'\0'}; static const WCHAR spaceW[] = {' ','\0'}; +/***************************************************************************** + * WCMD_strrev + * + * Reverse a WCHARacter string in-place (strrev() is not available under unixen :-( ). + */ +WCHAR * WCMD_strrev (WCHAR *buff) { + + int r, i; + WCHAR b; + + r = strlenW (buff); + for (i=0; idwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || + (fileb->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))) + { + BOOL aDir = filea->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + if (aDir) result = -1; + else result = 1; + if (orderGroupDirsReverse) result = -result; + return result; + + /* Order by Name: */ + } else if (dirOrder == Name) { + result = lstrcmpiW(filea->cFileName, fileb->cFileName); + + /* Order by Size: */ + } else if (dirOrder == Size) { + ULONG64 sizea = (((ULONG64)filea->nFileSizeHigh) << 32) + filea->nFileSizeLow; + ULONG64 sizeb = (((ULONG64)fileb->nFileSizeHigh) << 32) + fileb->nFileSizeLow; + if( sizea < sizeb ) result = -1; + else if( sizea == sizeb ) result = 0; + else result = 1; + + /* Order by Date: (Takes into account which date (/T option) */ + } else if (dirOrder == Date) { + + FILETIME *ft; + ULONG64 timea, timeb; + + if (dirTime == Written) { + ft = &filea->ftLastWriteTime; + timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + ft = &fileb->ftLastWriteTime; + timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + } else if (dirTime == Access) { + ft = &filea->ftLastAccessTime; + timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + ft = &fileb->ftLastAccessTime; + timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + } else { + ft = &filea->ftCreationTime; + timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + ft = &fileb->ftCreationTime; + timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + } + if( timea < timeb ) result = -1; + else if( timea == timeb ) result = 0; + else result = 1; + + /* Order by Extension: (Takes into account which date (/T option) */ + } else if (dirOrder == Extension) { + WCHAR drive[10]; + WCHAR dir[MAX_PATH]; + WCHAR fname[MAX_PATH]; + WCHAR extA[MAX_PATH]; + WCHAR extB[MAX_PATH]; + + /* Split into components */ + WCMD_splitpath(filea->cFileName, drive, dir, fname, extA); + WCMD_splitpath(fileb->cFileName, drive, dir, fname, extB); + result = lstrcmpiW(extA, extB); + } + + if (orderReverse) result = -result; + return result; +} + +/***************************************************************************** + * WCMD_getfileowner + * + * Reverse a WCHARacter string in-place (strrev() is not available under unixen :-( ). + */ +void WCMD_getfileowner(WCHAR *filename, WCHAR *owner, int ownerlen) { + + ULONG sizeNeeded = 0; + DWORD rc; + WCHAR name[MAXSTRING]; + WCHAR domain[MAXSTRING]; + + /* In case of error, return empty string */ + *owner = 0x00; + + /* Find out how much space we need for the owner security descriptor */ + GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, 0, 0, &sizeNeeded); + rc = GetLastError(); + + if(rc == ERROR_INSUFFICIENT_BUFFER && sizeNeeded > 0) { + + LPBYTE secBuffer; + PSID pSID = NULL; + BOOL defaulted = FALSE; + ULONG nameLen = MAXSTRING; + ULONG domainLen = MAXSTRING; + SID_NAME_USE nameuse; + + secBuffer = HeapAlloc(GetProcessHeap(),0,sizeNeeded * sizeof(BYTE)); + if(!secBuffer) return; + + /* Get the owners security descriptor */ + if(!GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, secBuffer, + sizeNeeded, &sizeNeeded)) { + HeapFree(GetProcessHeap(),0,secBuffer); + return; + } + + /* Get the SID from the SD */ + if(!GetSecurityDescriptorOwner(secBuffer, &pSID, &defaulted)) { + HeapFree(GetProcessHeap(),0,secBuffer); + return; + } + + /* Convert to a username */ + if (LookupAccountSid(NULL, pSID, name, &nameLen, domain, &domainLen, &nameuse)) { + static const WCHAR fmt[] = {'%','s','%','c','%','s','\0'}; + snprintfW(owner, ownerlen, fmt, domain, '\\', name); + } + HeapFree(GetProcessHeap(),0,secBuffer); + } + return; +} + +/***************************************************************************** + * WCMD_list_directory + * + * List a single file directory. This function (and those below it) can be called + * recursively when the /S switch is used. + * + * FIXME: Assumes 24-line display for the /P qualifier. + */ + +static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int level) { + + WCHAR string[1024], datestring[32], timestring[32]; + WCHAR real_path[MAX_PATH]; + WIN32_FIND_DATA *fd; + FILETIME ft; + SYSTEMTIME st; + HANDLE hff; + int dir_count, file_count, entry_count, i, widest, cur_width, tmp_width; + int numCols, numRows; + int rows, cols; + ULARGE_INTEGER byte_count, file_size; + DIRECTORY_STACK *parms; + int concurrentDirs = 0; + BOOL done_header = FALSE; + + static const WCHAR fmtDir[] = {'%','1','0','s',' ',' ','%','8','s',' ',' ', + '<','D','I','R','>',' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'}; + static const WCHAR fmtFile[] = {'%','1','0','s',' ',' ','%','8','s',' ',' ', + ' ',' ','%','1','0','s',' ',' ','\0'}; + static const WCHAR fmt2[] = {'%','-','1','3','s','\0'}; + static const WCHAR fmt3[] = {'%','-','2','3','s','\0'}; + static const WCHAR fmt4[] = {'%','s','\0'}; + static const WCHAR fmt5[] = {'%','s','%','s','\0'}; + + dir_count = 0; + file_count = 0; + entry_count = 0; + byte_count.QuadPart = 0; + widest = 0; + cur_width = 0; + + /* Loop merging all the files from consecutive parms which relate to the + same directory. Note issuing a directory header with no contents + mirrors what windows does */ + parms = inputparms; + fd = HeapAlloc(GetProcessHeap(),0,sizeof(WIN32_FIND_DATA)); + while (parms && strcmpW(inputparms->dirName, parms->dirName) == 0) { + concurrentDirs++; + + /* Work out the full path + filename */ + strcpyW(real_path, parms->dirName); + strcatW(real_path, parms->fileName); + + /* Load all files into an in memory structure */ + WINE_TRACE("Looking for matches to '%s'\n", wine_dbgstr_w(real_path)); + hff = FindFirstFile (real_path, (fd+entry_count)); + if (hff != INVALID_HANDLE_VALUE) { + do { + /* Skip any which are filtered out by attribute */ + if (((fd+entry_count)->dwFileAttributes & attrsbits) != showattrs) continue; + + entry_count++; + + /* Keep running track of longest filename for wide output */ + if (wide || orderByCol) { + int tmpLen = strlenW((fd+(entry_count-1))->cFileName) + 3; + if ((fd+(entry_count-1))->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) tmpLen = tmpLen + 2; + if (tmpLen > widest) widest = tmpLen; + } + + fd = HeapReAlloc(GetProcessHeap(),0,fd,(entry_count+1)*sizeof(WIN32_FIND_DATA)); + if (fd == NULL) { + FindClose (hff); + WINE_ERR("Out of memory\n"); + errorlevel = 1; + return parms->next; + } + } while (FindNextFile(hff, (fd+entry_count)) != 0); + FindClose (hff); + } + + /* Work out the actual current directory name without a trailing \ */ + strcpyW(real_path, parms->dirName); + real_path[strlenW(parms->dirName)-1] = 0x00; + + /* Output the results */ + if (!bare) { + if (level != 0 && (entry_count > 0)) WCMD_output (newline); + if (!recurse || ((entry_count > 0) && done_header==FALSE)) { + static const WCHAR headerW[] = {'D','i','r','e','c','t','o','r','y',' ','o','f', + ' ','%','s','\n','\n','\0'}; + WCMD_output (headerW, real_path); + done_header = TRUE; + } + } + + /* Move to next parm */ + parms = parms->next; + } + + /* Handle case where everything is filtered out */ + if (entry_count > 0) { + + /* Sort the list of files */ + qsort (fd, entry_count, sizeof(WIN32_FIND_DATA), WCMD_dir_sort); + + /* Work out the number of columns */ + WINE_TRACE("%d entries, maxwidth=%d, widest=%d\n", entry_count, max_width, widest); + if (wide || orderByCol) { + numCols = max(1, (int)max_width / widest); + numRows = entry_count / numCols; + if (entry_count % numCols) numRows++; + } else { + numCols = 1; + numRows = entry_count; + } + WINE_TRACE("cols=%d, rows=%d\n", numCols, numRows); + + for (rows=0; rows= entry_count) continue; + } else { + i = (rows * numCols) + cols; + if (i >= entry_count) continue; + } + + /* /L convers all names to lower case */ + if (lower) { + WCHAR *p = (fd+i)->cFileName; + while ( (*p = tolower(*p)) ) ++p; + } + + /* /Q gets file ownership information */ + if (usernames) { + strcpyW (string, inputparms->dirName); + strcatW (string, (fd+i)->cFileName); + WCMD_getfileowner(string, username, sizeof(username)/sizeof(WCHAR)); + } + + if (dirTime == Written) { + FileTimeToLocalFileTime (&(fd+i)->ftLastWriteTime, &ft); + } else if (dirTime == Access) { + FileTimeToLocalFileTime (&(fd+i)->ftLastAccessTime, &ft); + } else { + FileTimeToLocalFileTime (&(fd+i)->ftCreationTime, &ft); + } + FileTimeToSystemTime (&ft, &st); + GetDateFormat (0, DATE_SHORTDATE, &st, NULL, datestring, + sizeof(datestring)/sizeof(WCHAR)); + GetTimeFormat (0, TIME_NOSECONDS, &st, + NULL, timestring, sizeof(timestring)/sizeof(WCHAR)); + + if (wide) { + + tmp_width = cur_width; + if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + static const WCHAR fmt[] = {'[','%','s',']','\0'}; + WCMD_output (fmt, (fd+i)->cFileName); + dir_count++; + tmp_width = tmp_width + strlenW((fd+i)->cFileName) + 2; + } else { + static const WCHAR fmt[] = {'%','s','\0'}; + WCMD_output (fmt, (fd+i)->cFileName); + tmp_width = tmp_width + strlenW((fd+i)->cFileName) ; + file_count++; + file_size.u.LowPart = (fd+i)->nFileSizeLow; + file_size.u.HighPart = (fd+i)->nFileSizeHigh; + byte_count.QuadPart += file_size.QuadPart; + } + cur_width = cur_width + widest; + + if ((cur_width + widest) > max_width) { + cur_width = 0; + } else { + int padding = cur_width - tmp_width; + int toWrite = 0; + WCHAR temp[101]; + + /* Note: WCMD_output uses wvsprintf which does not allow %* + so manually pad with spaces to appropriate width */ + strcpyW(temp, emptyW); + while (padding > 0) { + strcatW(&temp[toWrite], spaceW); + toWrite++; + if (toWrite > 99) { + WCMD_output(temp); + toWrite = 0; + strcpyW(temp, emptyW); + } + padding--; + } + WCMD_output(temp); + } + + } else if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + dir_count++; + + if (!bare) { + WCMD_output (fmtDir, datestring, timestring); + if (shortname) WCMD_output (fmt2, (fd+i)->cAlternateFileName); + if (usernames) WCMD_output (fmt3, username); + WCMD_output(fmt4,(fd+i)->cFileName); + } else { + if (!((strcmpW((fd+i)->cFileName, dotW) == 0) || + (strcmpW((fd+i)->cFileName, dotdotW) == 0))) { + WCMD_output (fmt5, recurse?inputparms->dirName:emptyW, (fd+i)->cFileName); + } else { + addNewLine = FALSE; + } + } + } + else { + file_count++; + file_size.u.LowPart = (fd+i)->nFileSizeLow; + file_size.u.HighPart = (fd+i)->nFileSizeHigh; + byte_count.QuadPart += file_size.QuadPart; + if (!bare) { + WCMD_output (fmtFile, datestring, timestring, + WCMD_filesize64(file_size.QuadPart)); + if (shortname) WCMD_output (fmt2, (fd+i)->cAlternateFileName); + if (usernames) WCMD_output (fmt3, username); + WCMD_output(fmt4,(fd+i)->cFileName); + } else { + WCMD_output (fmt5, recurse?inputparms->dirName:emptyW, (fd+i)->cFileName); + } + } + } + if (addNewLine) WCMD_output (newline); + cur_width = 0; + } + + if (!bare) { + if (file_count == 1) { + static const WCHAR fmt[] = {' ',' ',' ',' ',' ',' ',' ','1',' ','f','i','l','e',' ', + '%','2','5','s',' ','b','y','t','e','s','\n','\0'}; + WCMD_output (fmt, WCMD_filesize64 (byte_count.QuadPart)); + } + else { + static const WCHAR fmt[] = {'%','8','d',' ','f','i','l','e','s',' ','%','2','4','s', + ' ','b','y','t','e','s','\n','\0'}; + WCMD_output (fmt, file_count, WCMD_filesize64 (byte_count.QuadPart)); + } + } + byte_total = byte_total + byte_count.QuadPart; + file_total = file_total + file_count; + dir_total = dir_total + dir_count; + + if (!bare && !recurse) { + if (dir_count == 1) { + static const WCHAR fmt[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','y', + ' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'}; + WCMD_output (fmt, 1); + } else { + static const WCHAR fmt[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','i', + 'e','s','\0'}; + WCMD_output (fmt, dir_count); + } + } + } + HeapFree(GetProcessHeap(),0,fd); + + /* When recursing, look in all subdirectories for matches */ + if (recurse) { + DIRECTORY_STACK *dirStack = NULL; + DIRECTORY_STACK *lastEntry = NULL; + WIN32_FIND_DATA finddata; + + /* Build path to search */ + strcpyW(string, inputparms->dirName); + strcatW(string, starW); + + WINE_TRACE("Recursive, looking for '%s'\n", wine_dbgstr_w(string)); + hff = FindFirstFile (string, &finddata); + if (hff != INVALID_HANDLE_VALUE) { + do { + if ((finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + (strcmpW(finddata.cFileName, dotdotW) != 0) && + (strcmpW(finddata.cFileName, dotW) != 0)) { + + DIRECTORY_STACK *thisDir; + int dirsToCopy = concurrentDirs; + + /* Loop creating list of subdirs for all concurrent entries */ + parms = inputparms; + while (dirsToCopy > 0) { + dirsToCopy--; + + /* Work out search parameter in sub dir */ + strcpyW (string, inputparms->dirName); + strcatW (string, finddata.cFileName); + strcatW (string, slashW); + WINE_TRACE("Recursive, Adding to search list '%s'\n", wine_dbgstr_w(string)); + + /* Allocate memory, add to list */ + thisDir = HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK)); + if (dirStack == NULL) dirStack = thisDir; + if (lastEntry != NULL) lastEntry->next = thisDir; + lastEntry = thisDir; + thisDir->next = NULL; + thisDir->dirName = HeapAlloc(GetProcessHeap(),0, + sizeof(WCHAR) * (strlenW(string)+1)); + strcpyW(thisDir->dirName, string); + thisDir->fileName = HeapAlloc(GetProcessHeap(),0, + sizeof(WCHAR) * (strlenW(parms->fileName)+1)); + strcpyW(thisDir->fileName, parms->fileName); + parms = parms->next; + } + } + } while (FindNextFile(hff, &finddata) != 0); + FindClose (hff); + + while (dirStack != NULL) { + DIRECTORY_STACK *thisDir = dirStack; + dirStack = WCMD_list_directory (thisDir, 1); + while (thisDir != dirStack) { + DIRECTORY_STACK *tempDir = thisDir->next; + HeapFree(GetProcessHeap(),0,thisDir->dirName); + HeapFree(GetProcessHeap(),0,thisDir->fileName); + HeapFree(GetProcessHeap(),0,thisDir); + thisDir = tempDir; + } + } + } + } + + /* Handle case where everything is filtered out */ + if ((file_total + dir_total == 0) && (level == 0)) { + SetLastError (ERROR_FILE_NOT_FOUND); + WCMD_print_error (); + errorlevel = 1; + } + + return parms; +} + +/***************************************************************************** + * WCMD_dir_trailer + * + * Print out the trailer for the supplied drive letter + */ +static void WCMD_dir_trailer(WCHAR drive) { + ULARGE_INTEGER avail, total, freebytes; + DWORD status; + WCHAR driveName[4] = {'c',':','\\','\0'}; + + driveName[0] = drive; + status = GetDiskFreeSpaceEx (driveName, &avail, &total, &freebytes); + WINE_TRACE("Writing trailer for '%s' gave %d(%d)\n", wine_dbgstr_w(driveName), + status, GetLastError()); + + if (errorlevel==0 && !bare) { + if (recurse) { + static const WCHAR fmt1[] = {'\n',' ',' ',' ',' ',' ','T','o','t','a','l',' ','f','i','l','e','s', + ' ','l','i','s','t','e','d',':','\n','%','8','d',' ','f','i','l','e', + 's','%','2','5','s',' ','b','y','t','e','s','\n','\0'}; + static const WCHAR fmt2[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','i','e','s',' ','%', + '1','8','s',' ','b','y','t','e','s',' ','f','r','e','e','\n','\n', + '\0'}; + WCMD_output (fmt1, file_total, WCMD_filesize64 (byte_total)); + WCMD_output (fmt2, dir_total, WCMD_filesize64 (freebytes.QuadPart)); + } else { + static const WCHAR fmt[] = {' ','%','1','8','s',' ','b','y','t','e','s',' ','f','r','e','e', + '\n','\n','\0'}; + WCMD_output (fmt, WCMD_filesize64 (freebytes.QuadPart)); + } + } +} + /***************************************************************************** * WCMD_directory * @@ -439,560 +987,3 @@ exit: HeapFree(GetProcessHeap(),0,prevEntry); } } - -/***************************************************************************** - * WCMD_list_directory - * - * List a single file directory. This function (and those below it) can be called - * recursively when the /S switch is used. - * - * FIXME: Assumes 24-line display for the /P qualifier. - */ - -static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int level) { - - WCHAR string[1024], datestring[32], timestring[32]; - WCHAR real_path[MAX_PATH]; - WIN32_FIND_DATA *fd; - FILETIME ft; - SYSTEMTIME st; - HANDLE hff; - int dir_count, file_count, entry_count, i, widest, cur_width, tmp_width; - int numCols, numRows; - int rows, cols; - ULARGE_INTEGER byte_count, file_size; - DIRECTORY_STACK *parms; - int concurrentDirs = 0; - BOOL done_header = FALSE; - - static const WCHAR fmtDir[] = {'%','1','0','s',' ',' ','%','8','s',' ',' ', - '<','D','I','R','>',' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'}; - static const WCHAR fmtFile[] = {'%','1','0','s',' ',' ','%','8','s',' ',' ', - ' ',' ','%','1','0','s',' ',' ','\0'}; - static const WCHAR fmt2[] = {'%','-','1','3','s','\0'}; - static const WCHAR fmt3[] = {'%','-','2','3','s','\0'}; - static const WCHAR fmt4[] = {'%','s','\0'}; - static const WCHAR fmt5[] = {'%','s','%','s','\0'}; - - dir_count = 0; - file_count = 0; - entry_count = 0; - byte_count.QuadPart = 0; - widest = 0; - cur_width = 0; - - /* Loop merging all the files from consecutive parms which relate to the - same directory. Note issuing a directory header with no contents - mirrors what windows does */ - parms = inputparms; - fd = HeapAlloc(GetProcessHeap(),0,sizeof(WIN32_FIND_DATA)); - while (parms && strcmpW(inputparms->dirName, parms->dirName) == 0) { - concurrentDirs++; - - /* Work out the full path + filename */ - strcpyW(real_path, parms->dirName); - strcatW(real_path, parms->fileName); - - /* Load all files into an in memory structure */ - WINE_TRACE("Looking for matches to '%s'\n", wine_dbgstr_w(real_path)); - hff = FindFirstFile (real_path, (fd+entry_count)); - if (hff != INVALID_HANDLE_VALUE) { - do { - /* Skip any which are filtered out by attribute */ - if (((fd+entry_count)->dwFileAttributes & attrsbits) != showattrs) continue; - - entry_count++; - - /* Keep running track of longest filename for wide output */ - if (wide || orderByCol) { - int tmpLen = strlenW((fd+(entry_count-1))->cFileName) + 3; - if ((fd+(entry_count-1))->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) tmpLen = tmpLen + 2; - if (tmpLen > widest) widest = tmpLen; - } - - fd = HeapReAlloc(GetProcessHeap(),0,fd,(entry_count+1)*sizeof(WIN32_FIND_DATA)); - if (fd == NULL) { - FindClose (hff); - WINE_ERR("Out of memory\n"); - errorlevel = 1; - return parms->next; - } - } while (FindNextFile(hff, (fd+entry_count)) != 0); - FindClose (hff); - } - - /* Work out the actual current directory name without a trailing \ */ - strcpyW(real_path, parms->dirName); - real_path[strlenW(parms->dirName)-1] = 0x00; - - /* Output the results */ - if (!bare) { - if (level != 0 && (entry_count > 0)) WCMD_output (newline); - if (!recurse || ((entry_count > 0) && done_header==FALSE)) { - static const WCHAR headerW[] = {'D','i','r','e','c','t','o','r','y',' ','o','f', - ' ','%','s','\n','\n','\0'}; - WCMD_output (headerW, real_path); - done_header = TRUE; - } - } - - /* Move to next parm */ - parms = parms->next; - } - - /* Handle case where everything is filtered out */ - if (entry_count > 0) { - - /* Sort the list of files */ - qsort (fd, entry_count, sizeof(WIN32_FIND_DATA), WCMD_dir_sort); - - /* Work out the number of columns */ - WINE_TRACE("%d entries, maxwidth=%d, widest=%d\n", entry_count, max_width, widest); - if (wide || orderByCol) { - numCols = max(1, (int)max_width / widest); - numRows = entry_count / numCols; - if (entry_count % numCols) numRows++; - } else { - numCols = 1; - numRows = entry_count; - } - WINE_TRACE("cols=%d, rows=%d\n", numCols, numRows); - - for (rows=0; rows= entry_count) continue; - } else { - i = (rows * numCols) + cols; - if (i >= entry_count) continue; - } - - /* /L convers all names to lower case */ - if (lower) { - WCHAR *p = (fd+i)->cFileName; - while ( (*p = tolower(*p)) ) ++p; - } - - /* /Q gets file ownership information */ - if (usernames) { - strcpyW (string, inputparms->dirName); - strcatW (string, (fd+i)->cFileName); - WCMD_getfileowner(string, username, sizeof(username)/sizeof(WCHAR)); - } - - if (dirTime == Written) { - FileTimeToLocalFileTime (&(fd+i)->ftLastWriteTime, &ft); - } else if (dirTime == Access) { - FileTimeToLocalFileTime (&(fd+i)->ftLastAccessTime, &ft); - } else { - FileTimeToLocalFileTime (&(fd+i)->ftCreationTime, &ft); - } - FileTimeToSystemTime (&ft, &st); - GetDateFormat (0, DATE_SHORTDATE, &st, NULL, datestring, - sizeof(datestring)/sizeof(WCHAR)); - GetTimeFormat (0, TIME_NOSECONDS, &st, - NULL, timestring, sizeof(timestring)/sizeof(WCHAR)); - - if (wide) { - - tmp_width = cur_width; - if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - static const WCHAR fmt[] = {'[','%','s',']','\0'}; - WCMD_output (fmt, (fd+i)->cFileName); - dir_count++; - tmp_width = tmp_width + strlenW((fd+i)->cFileName) + 2; - } else { - static const WCHAR fmt[] = {'%','s','\0'}; - WCMD_output (fmt, (fd+i)->cFileName); - tmp_width = tmp_width + strlenW((fd+i)->cFileName) ; - file_count++; - file_size.u.LowPart = (fd+i)->nFileSizeLow; - file_size.u.HighPart = (fd+i)->nFileSizeHigh; - byte_count.QuadPart += file_size.QuadPart; - } - cur_width = cur_width + widest; - - if ((cur_width + widest) > max_width) { - cur_width = 0; - } else { - int padding = cur_width - tmp_width; - int toWrite = 0; - WCHAR temp[101]; - - /* Note: WCMD_output uses wvsprintf which does not allow %* - so manually pad with spaces to appropriate width */ - strcpyW(temp, emptyW); - while (padding > 0) { - strcatW(&temp[toWrite], spaceW); - toWrite++; - if (toWrite > 99) { - WCMD_output(temp); - toWrite = 0; - strcpyW(temp, emptyW); - } - padding--; - } - WCMD_output(temp); - } - - } else if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - dir_count++; - - if (!bare) { - WCMD_output (fmtDir, datestring, timestring); - if (shortname) WCMD_output (fmt2, (fd+i)->cAlternateFileName); - if (usernames) WCMD_output (fmt3, username); - WCMD_output(fmt4,(fd+i)->cFileName); - } else { - if (!((strcmpW((fd+i)->cFileName, dotW) == 0) || - (strcmpW((fd+i)->cFileName, dotdotW) == 0))) { - WCMD_output (fmt5, recurse?inputparms->dirName:emptyW, (fd+i)->cFileName); - } else { - addNewLine = FALSE; - } - } - } - else { - file_count++; - file_size.u.LowPart = (fd+i)->nFileSizeLow; - file_size.u.HighPart = (fd+i)->nFileSizeHigh; - byte_count.QuadPart += file_size.QuadPart; - if (!bare) { - WCMD_output (fmtFile, datestring, timestring, - WCMD_filesize64(file_size.QuadPart)); - if (shortname) WCMD_output (fmt2, (fd+i)->cAlternateFileName); - if (usernames) WCMD_output (fmt3, username); - WCMD_output(fmt4,(fd+i)->cFileName); - } else { - WCMD_output (fmt5, recurse?inputparms->dirName:emptyW, (fd+i)->cFileName); - } - } - } - if (addNewLine) WCMD_output (newline); - cur_width = 0; - } - - if (!bare) { - if (file_count == 1) { - static const WCHAR fmt[] = {' ',' ',' ',' ',' ',' ',' ','1',' ','f','i','l','e',' ', - '%','2','5','s',' ','b','y','t','e','s','\n','\0'}; - WCMD_output (fmt, WCMD_filesize64 (byte_count.QuadPart)); - } - else { - static const WCHAR fmt[] = {'%','8','d',' ','f','i','l','e','s',' ','%','2','4','s', - ' ','b','y','t','e','s','\n','\0'}; - WCMD_output (fmt, file_count, WCMD_filesize64 (byte_count.QuadPart)); - } - } - byte_total = byte_total + byte_count.QuadPart; - file_total = file_total + file_count; - dir_total = dir_total + dir_count; - - if (!bare && !recurse) { - if (dir_count == 1) { - static const WCHAR fmt[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','y', - ' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'}; - WCMD_output (fmt, 1); - } else { - static const WCHAR fmt[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','i', - 'e','s','\0'}; - WCMD_output (fmt, dir_count); - } - } - } - HeapFree(GetProcessHeap(),0,fd); - - /* When recursing, look in all subdirectories for matches */ - if (recurse) { - DIRECTORY_STACK *dirStack = NULL; - DIRECTORY_STACK *lastEntry = NULL; - WIN32_FIND_DATA finddata; - - /* Build path to search */ - strcpyW(string, inputparms->dirName); - strcatW(string, starW); - - WINE_TRACE("Recursive, looking for '%s'\n", wine_dbgstr_w(string)); - hff = FindFirstFile (string, &finddata); - if (hff != INVALID_HANDLE_VALUE) { - do { - if ((finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && - (strcmpW(finddata.cFileName, dotdotW) != 0) && - (strcmpW(finddata.cFileName, dotW) != 0)) { - - DIRECTORY_STACK *thisDir; - int dirsToCopy = concurrentDirs; - - /* Loop creating list of subdirs for all concurrent entries */ - parms = inputparms; - while (dirsToCopy > 0) { - dirsToCopy--; - - /* Work out search parameter in sub dir */ - strcpyW (string, inputparms->dirName); - strcatW (string, finddata.cFileName); - strcatW (string, slashW); - WINE_TRACE("Recursive, Adding to search list '%s'\n", wine_dbgstr_w(string)); - - /* Allocate memory, add to list */ - thisDir = HeapAlloc(GetProcessHeap(),0,sizeof(DIRECTORY_STACK)); - if (dirStack == NULL) dirStack = thisDir; - if (lastEntry != NULL) lastEntry->next = thisDir; - lastEntry = thisDir; - thisDir->next = NULL; - thisDir->dirName = HeapAlloc(GetProcessHeap(),0, - sizeof(WCHAR) * (strlenW(string)+1)); - strcpyW(thisDir->dirName, string); - thisDir->fileName = HeapAlloc(GetProcessHeap(),0, - sizeof(WCHAR) * (strlenW(parms->fileName)+1)); - strcpyW(thisDir->fileName, parms->fileName); - parms = parms->next; - } - } - } while (FindNextFile(hff, &finddata) != 0); - FindClose (hff); - - while (dirStack != NULL) { - DIRECTORY_STACK *thisDir = dirStack; - dirStack = WCMD_list_directory (thisDir, 1); - while (thisDir != dirStack) { - DIRECTORY_STACK *tempDir = thisDir->next; - HeapFree(GetProcessHeap(),0,thisDir->dirName); - HeapFree(GetProcessHeap(),0,thisDir->fileName); - HeapFree(GetProcessHeap(),0,thisDir); - thisDir = tempDir; - } - } - } - } - - /* Handle case where everything is filtered out */ - if ((file_total + dir_total == 0) && (level == 0)) { - SetLastError (ERROR_FILE_NOT_FOUND); - WCMD_print_error (); - errorlevel = 1; - } - - return parms; -} - -/***************************************************************************** - * WCMD_filesize64 - * - * Convert a 64-bit number into a WCHARacter string, with commas every three digits. - * Result is returned in a static string overwritten with each call. - * FIXME: There must be a better algorithm! - */ - -WCHAR * WCMD_filesize64 (ULONGLONG n) { - - ULONGLONG q; - unsigned int r, i; - WCHAR *p; - static WCHAR buff[32]; - - p = buff; - i = -3; - do { - if (separator && ((++i)%3 == 1)) *p++ = ','; - q = n / 10; - r = n - (q * 10); - *p++ = r + '0'; - *p = '\0'; - n = q; - } while (n != 0); - WCMD_strrev (buff); - return buff; -} - -/***************************************************************************** - * WCMD_strrev - * - * Reverse a WCHARacter string in-place (strrev() is not available under unixen :-( ). - */ - -WCHAR * WCMD_strrev (WCHAR *buff) { - - int r, i; - WCHAR b; - - r = strlenW (buff); - for (i=0; idwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || - (fileb->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))) - { - BOOL aDir = filea->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - if (aDir) result = -1; - else result = 1; - if (orderGroupDirsReverse) result = -result; - return result; - - /* Order by Name: */ - } else if (dirOrder == Name) { - result = lstrcmpiW(filea->cFileName, fileb->cFileName); - - /* Order by Size: */ - } else if (dirOrder == Size) { - ULONG64 sizea = (((ULONG64)filea->nFileSizeHigh) << 32) + filea->nFileSizeLow; - ULONG64 sizeb = (((ULONG64)fileb->nFileSizeHigh) << 32) + fileb->nFileSizeLow; - if( sizea < sizeb ) result = -1; - else if( sizea == sizeb ) result = 0; - else result = 1; - - /* Order by Date: (Takes into account which date (/T option) */ - } else if (dirOrder == Date) { - - FILETIME *ft; - ULONG64 timea, timeb; - - if (dirTime == Written) { - ft = &filea->ftLastWriteTime; - timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - ft = &fileb->ftLastWriteTime; - timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - } else if (dirTime == Access) { - ft = &filea->ftLastAccessTime; - timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - ft = &fileb->ftLastAccessTime; - timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - } else { - ft = &filea->ftCreationTime; - timea = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - ft = &fileb->ftCreationTime; - timeb = (((ULONG64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - } - if( timea < timeb ) result = -1; - else if( timea == timeb ) result = 0; - else result = 1; - - /* Order by Extension: (Takes into account which date (/T option) */ - } else if (dirOrder == Extension) { - WCHAR drive[10]; - WCHAR dir[MAX_PATH]; - WCHAR fname[MAX_PATH]; - WCHAR extA[MAX_PATH]; - WCHAR extB[MAX_PATH]; - - /* Split into components */ - WCMD_splitpath(filea->cFileName, drive, dir, fname, extA); - WCMD_splitpath(fileb->cFileName, drive, dir, fname, extB); - result = lstrcmpiW(extA, extB); - } - - if (orderReverse) result = -result; - return result; -} - -/***************************************************************************** - * WCMD_getfileowner - * - * Reverse a WCHARacter string in-place (strrev() is not available under unixen :-( ). - */ -void WCMD_getfileowner(WCHAR *filename, WCHAR *owner, int ownerlen) { - - ULONG sizeNeeded = 0; - DWORD rc; - WCHAR name[MAXSTRING]; - WCHAR domain[MAXSTRING]; - - /* In case of error, return empty string */ - *owner = 0x00; - - /* Find out how much space we need for the owner security descriptor */ - GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, 0, 0, &sizeNeeded); - rc = GetLastError(); - - if(rc == ERROR_INSUFFICIENT_BUFFER && sizeNeeded > 0) { - - LPBYTE secBuffer; - PSID pSID = NULL; - BOOL defaulted = FALSE; - ULONG nameLen = MAXSTRING; - ULONG domainLen = MAXSTRING; - SID_NAME_USE nameuse; - - secBuffer = HeapAlloc(GetProcessHeap(),0,sizeNeeded * sizeof(BYTE)); - if(!secBuffer) return; - - /* Get the owners security descriptor */ - if(!GetFileSecurity(filename, OWNER_SECURITY_INFORMATION, secBuffer, - sizeNeeded, &sizeNeeded)) { - HeapFree(GetProcessHeap(),0,secBuffer); - return; - } - - /* Get the SID from the SD */ - if(!GetSecurityDescriptorOwner(secBuffer, &pSID, &defaulted)) { - HeapFree(GetProcessHeap(),0,secBuffer); - return; - } - - /* Convert to a username */ - if (LookupAccountSid(NULL, pSID, name, &nameLen, domain, &domainLen, &nameuse)) { - static const WCHAR fmt[] = {'%','s','%','c','%','s','\0'}; - snprintfW(owner, ownerlen, fmt, domain, '\\', name); - } - HeapFree(GetProcessHeap(),0,secBuffer); - } - return; -} - -/***************************************************************************** - * WCMD_dir_trailer - * - * Print out the trailer for the supplied drive letter - */ -static void WCMD_dir_trailer(WCHAR drive) { - ULARGE_INTEGER avail, total, freebytes; - DWORD status; - WCHAR driveName[4] = {'c',':','\\','\0'}; - - driveName[0] = drive; - status = GetDiskFreeSpaceEx (driveName, &avail, &total, &freebytes); - WINE_TRACE("Writing trailer for '%s' gave %d(%d)\n", wine_dbgstr_w(driveName), - status, GetLastError()); - - if (errorlevel==0 && !bare) { - if (recurse) { - static const WCHAR fmt1[] = {'\n',' ',' ',' ',' ',' ','T','o','t','a','l',' ','f','i','l','e','s', - ' ','l','i','s','t','e','d',':','\n','%','8','d',' ','f','i','l','e', - 's','%','2','5','s',' ','b','y','t','e','s','\n','\0'}; - static const WCHAR fmt2[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','i','e','s',' ','%', - '1','8','s',' ','b','y','t','e','s',' ','f','r','e','e','\n','\n', - '\0'}; - WCMD_output (fmt1, file_total, WCMD_filesize64 (byte_total)); - WCMD_output (fmt2, dir_total, WCMD_filesize64 (freebytes.QuadPart)); - } else { - static const WCHAR fmt[] = {' ','%','1','8','s',' ','b','y','t','e','s',' ','f','r','e','e', - '\n','\n','\0'}; - WCMD_output (fmt, WCMD_filesize64 (freebytes.QuadPart)); - } - } -}