123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- /*
- * ATTRIB - Wine-compatible attrib program
- *
- * Copyright 2010-2012 Christian Costa
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
- #include <windows.h>
- #include <wine/debug.h>
- #include "attrib.h"
- WINE_DEFAULT_DEBUG_CHANNEL(attrib);
- /* =========================================================================
- * Load a string from the resource file, handling any error
- * Returns string retrieved from resource file
- * ========================================================================= */
- static WCHAR *ATTRIB_LoadMessage(UINT id)
- {
- static WCHAR msg[MAXSTRING];
- if (!LoadStringW(GetModuleHandleW(NULL), id, msg, ARRAY_SIZE(msg))) {
- WINE_FIXME("LoadString failed with %ld\n", GetLastError());
- lstrcpyW(msg, L"Failed!");
- }
- return msg;
- }
- /* =========================================================================
- * Output a formatted unicode string. Ideally this will go to the console
- * and hence required WriteConsoleW to output it, however if file i/o is
- * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
- * ========================================================================= */
- static int WINAPIV ATTRIB_wprintf(const WCHAR *format, ...)
- {
- static WCHAR *output_bufW = NULL;
- static char *output_bufA = NULL;
- static BOOL toConsole = TRUE;
- static BOOL traceOutput = FALSE;
- #define MAX_WRITECONSOLE_SIZE 65535
- va_list parms;
- DWORD nOut;
- int len;
- DWORD res = 0;
- /*
- * Allocate buffer to use when writing to console
- * Note: Not freed - memory will be allocated once and released when
- * attrib ends
- */
- if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
- MAX_WRITECONSOLE_SIZE*sizeof(WCHAR));
- if (!output_bufW) {
- WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
- return 0;
- }
- va_start(parms, format);
- len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_bufW,
- MAX_WRITECONSOLE_SIZE/sizeof(*output_bufW), &parms);
- va_end(parms);
- if (len == 0 && GetLastError() != ERROR_NO_WORK_DONE) {
- WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
- return 0;
- }
- /* Try to write as unicode all the time we think it's a console */
- if (toConsole) {
- res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
- output_bufW, len, &nOut, NULL);
- }
- /* If writing to console has failed (ever) we assume it's file
- i/o so convert to OEM codepage and output */
- if (!res) {
- BOOL usedDefaultChar = FALSE;
- DWORD convertedChars;
- toConsole = FALSE;
- /*
- * Allocate buffer to use when writing to file. Not freed, as above
- */
- if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
- MAX_WRITECONSOLE_SIZE);
- if (!output_bufA) {
- WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
- return 0;
- }
- /* Convert to OEM, then output */
- convertedChars = WideCharToMultiByte(GetOEMCP(), 0, output_bufW,
- len, output_bufA, MAX_WRITECONSOLE_SIZE,
- "?", &usedDefaultChar);
- WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
- &nOut, FALSE);
- }
- /* Trace whether screen or console */
- if (!traceOutput) {
- WINE_TRACE("Writing to console? (%d)\n", toConsole);
- traceOutput = TRUE;
- }
- return nOut;
- }
- /* =========================================================================
- * Handle the processing for a single directory, optionally recursing into
- * subdirectories if needed.
- * Parameters:
- * rootdir [I] The directory to search in
- * filespec [I] The filespec to search for
- * recurse [I] Whether to recurse (search subdirectories before
- * current directory)
- * includedirs [I] Whether to set directory attributes as well
- * attrib_set [I] Attributes to set
- * attrib_clear [I] Attributes to clear
- *
- * Returns TRUE if at least one file displayed / modified
- * ========================================================================= */
- static BOOL ATTRIB_processdirectory(const WCHAR *rootdir, const WCHAR *filespec,
- BOOL recurse, BOOL includedirs,
- DWORD attrib_set, DWORD attrib_clear)
- {
- BOOL found = FALSE;
- WCHAR buffer[MAX_PATH];
- HANDLE hff;
- WIN32_FIND_DATAW fd;
- WCHAR flags[] = L" ";
- WINE_TRACE("Processing dir '%s', spec '%s', %d,%lx,%lx\n",
- wine_dbgstr_w(rootdir), wine_dbgstr_w(filespec),
- recurse, attrib_set, attrib_clear);
- if (recurse) {
- /* Build spec to search for */
- lstrcpyW(buffer, rootdir);
- lstrcatW(buffer, L"*");
- /* Search for directories in the location and recurse if necessary */
- WINE_TRACE("Searching for directories with '%s'\n", wine_dbgstr_w(buffer));
- hff = FindFirstFileW(buffer, &fd);
- if (hff != INVALID_HANDLE_VALUE) {
- do {
- /* Only interested in directories, and not . nor .. */
- if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
- !lstrcmpW(fd.cFileName, L".") || !lstrcmpW(fd.cFileName, L".."))
- continue;
- /* Build new root dir to go searching in */
- lstrcpyW(buffer, rootdir);
- lstrcatW(buffer, fd.cFileName);
- lstrcatW(buffer, L"\\");
- ATTRIB_processdirectory(buffer, filespec, recurse, includedirs,
- attrib_set, attrib_clear);
- } while (FindNextFileW(hff, &fd) != 0);
- }
- FindClose (hff);
- }
- /* Build spec to search for */
- lstrcpyW(buffer, rootdir);
- lstrcatW(buffer, filespec);
- WINE_TRACE("Searching for files as '%s'\n", wine_dbgstr_w(buffer));
- /* Search for files in the location with the filespec supplied */
- hff = FindFirstFileW(buffer, &fd);
- if (hff != INVALID_HANDLE_VALUE) {
- do {
- DWORD count;
- WINE_TRACE("Found '%s'\n", wine_dbgstr_w(fd.cFileName));
- if (!lstrcmpW(fd.cFileName, L".") || !lstrcmpW(fd.cFileName, L".."))
- continue;
- if (!includedirs && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- continue;
- if (attrib_set || attrib_clear) {
- fd.dwFileAttributes &= ~attrib_clear;
- fd.dwFileAttributes |= attrib_set;
- if (!fd.dwFileAttributes)
- fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
- lstrcpyW(buffer, rootdir);
- lstrcatW(buffer, fd.cFileName);
- SetFileAttributesW(buffer, fd.dwFileAttributes);
- found = TRUE;
- } else {
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
- flags[4] = 'H';
- }
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
- flags[1] = 'S';
- }
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
- flags[0] = 'A';
- }
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
- flags[5] = 'R';
- }
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
- flags[6] = 'T';
- }
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) {
- flags[7] = 'C';
- }
- lstrcpyW(buffer, rootdir);
- lstrcatW(buffer, fd.cFileName);
- ATTRIB_wprintf(L"%1 %2\n", flags, buffer);
- for (count = 0; count < (ARRAY_SIZE(flags) - 1); count++) flags[count] = ' ';
- found = TRUE;
- }
- } while (FindNextFileW(hff, &fd) != 0);
- }
- FindClose (hff);
- return found;
- }
- int __cdecl wmain(int argc, WCHAR *argv[])
- {
- WCHAR name[MAX_PATH];
- WCHAR *namepart;
- WCHAR curdir[MAX_PATH];
- WCHAR originalname[MAX_PATH];
- DWORD attrib_set = 0;
- DWORD attrib_clear = 0;
- BOOL attrib_recurse = FALSE;
- BOOL attrib_includedirs = FALSE;
- int i = 1;
- BOOL found = FALSE;
- if ((argc >= 2) && !lstrcmpW(argv[1], L"/?")) {
- ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_HELP));
- return 0;
- }
- /* By default all files from current directory are taken into account */
- lstrcpyW(originalname, L".\\*");
- while (i < argc) {
- WCHAR *param = argv[i++];
- WINE_TRACE("Processing arg: '%s'\n", wine_dbgstr_w(param));
- if ((param[0] == '+') || (param[0] == '-')) {
- DWORD attrib = 0;
- switch (param[1]) {
- case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
- case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
- case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
- case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
- default:
- ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_NYI));
- return 0;
- }
- switch (param[0]) {
- case '+': attrib_set = attrib; break;
- case '-': attrib_clear = attrib; break;
- }
- } else if (param[0] == '/') {
- if (((param[1] == 'D') || (param[1] == 'd')) && !param[2]) {
- attrib_includedirs = TRUE;
- } else if (((param[1] == 'S') || (param[1] == 's')) && !param[2]) {
- attrib_recurse = TRUE;
- } else {
- WINE_FIXME("Unknown option %s\n", debugstr_w(param));
- }
- } else if (param[0]) {
- lstrcpyW(originalname, param);
- }
- }
- /* Name may be a relative or explicit path, so calculate curdir based on
- current locations, stripping off the filename */
- WINE_TRACE("Supplied name: '%s'\n", wine_dbgstr_w(originalname));
- GetFullPathNameW(originalname, ARRAY_SIZE(curdir), curdir, &namepart);
- WINE_TRACE("Result: '%s'\n", wine_dbgstr_w(curdir));
- if (namepart) {
- lstrcpyW(name, namepart);
- *namepart = 0;
- } else {
- name[0] = 0;
- }
- /* If a directory is explicitly supplied on the command line, and no
- wildcards are in the name, then allow it to be changed/displayed */
- if (wcspbrk(originalname, L"*?") == NULL) attrib_includedirs = TRUE;
- /* Do all the processing based on the filename arg */
- found = ATTRIB_processdirectory(curdir, name, attrib_recurse,
- attrib_includedirs, attrib_set, attrib_clear);
- if (!found) {
- ATTRIB_wprintf(ATTRIB_LoadMessage(STRING_FILENOTFOUND), originalname);
- }
- return 0;
- }
|