1 /* 2 * ATTRIB.C - attrib internal command. 3 * 4 * 5 * History: 6 * 7 * 04-Dec-1998 Eric Kohl 8 * started 9 * 10 * 09-Dec-1998 Eric Kohl 11 * implementation works, except recursion ("attrib /s"). 12 * 13 * 05-Jan-1999 Eric Kohl 14 * major rewrite. 15 * fixed recursion ("attrib /s"). 16 * started directory support ("attrib /s /d"). 17 * updated help text. 18 * 19 * 14-Jan-1999 Eric Kohl 20 * Unicode ready! 21 * 22 * 19-Jan-1999 Eric Kohl 23 * Redirection ready! 24 * 25 * 21-Jan-1999 Eric Kohl 26 * Added check for invalid filenames. 27 * 28 * 23-Jan-1999 Eric Kohl 29 * Added handling of multiple filenames. 30 * 31 * 02-Apr-2005 (Magnus Olsen <magnus@greatlord.com>) 32 * Remove all hardcoded strings in En.rc 33 */ 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 38 #include <windef.h> 39 #include <winbase.h> 40 #include <wincon.h> 41 #include <winuser.h> 42 43 #include <conutils.h> 44 45 #include "resource.h" 46 47 CON_SCREEN StdOutScreen = INIT_CON_SCREEN(StdOut); 48 49 static 50 VOID 51 ErrorMessage( 52 DWORD dwErrorCode, 53 LPWSTR szFormat, 54 ...) 55 { 56 WCHAR szMsg[RC_STRING_MAX_SIZE]; 57 WCHAR szMessage[1024]; 58 LPWSTR szError; 59 va_list arg_ptr; 60 61 if (dwErrorCode == ERROR_SUCCESS) 62 return; 63 64 if (szFormat) 65 { 66 va_start(arg_ptr, szFormat); 67 vswprintf(szMessage, szFormat, arg_ptr); 68 va_end(arg_ptr); 69 } 70 71 if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 72 NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 73 (LPWSTR)&szError, 0, NULL)) 74 { 75 ConPrintf(StdErr, L"%s %s\n", szError, szMessage); 76 if (szError) 77 LocalFree(szError); 78 return; 79 } 80 81 /* Fall back just in case the error is not defined */ 82 LoadStringW(GetModuleHandle(NULL), STRING_CONSOLE_ERROR, szMsg, ARRAYSIZE(szMsg)); 83 if (szFormat) 84 ConPrintf(StdErr, L"%s -- %s\n", szMsg, szMessage); 85 else 86 ConPrintf(StdErr, L"%s\n", szMsg); 87 } 88 89 static 90 INT 91 PrintAttribute( 92 LPWSTR pszPath, 93 LPWSTR pszFile, 94 BOOL bRecurse) 95 { 96 WIN32_FIND_DATAW findData; 97 HANDLE hFind; 98 WCHAR szFullName[MAX_PATH]; 99 LPWSTR pszFileName; 100 101 /* prepare full file name buffer */ 102 wcscpy(szFullName, pszPath); 103 pszFileName = szFullName + wcslen(szFullName); 104 105 /* display all subdirectories */ 106 if (bRecurse) 107 { 108 /* append file name */ 109 wcscpy(pszFileName, pszFile); 110 111 hFind = FindFirstFileW(szFullName, &findData); 112 if (hFind == INVALID_HANDLE_VALUE) 113 { 114 ErrorMessage(GetLastError(), pszFile); 115 return 1; 116 } 117 118 do 119 { 120 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 121 continue; 122 123 if (!wcscmp(findData.cFileName, L".") || 124 !wcscmp(findData.cFileName, L"..")) 125 continue; 126 127 wcscpy(pszFileName, findData.cFileName); 128 wcscat(pszFileName, L"\\"); 129 PrintAttribute(szFullName, pszFile, bRecurse); 130 } 131 while(FindNextFileW(hFind, &findData)); 132 FindClose(hFind); 133 } 134 135 /* append file name */ 136 wcscpy(pszFileName, pszFile); 137 138 /* display current directory */ 139 hFind = FindFirstFileW(szFullName, &findData); 140 if (hFind == INVALID_HANDLE_VALUE) 141 { 142 ErrorMessage(GetLastError(), pszFile); 143 return 1; 144 } 145 146 do 147 { 148 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 149 continue; 150 151 wcscpy(pszFileName, findData.cFileName); 152 153 ConPrintf(StdOut, 154 L"%c %c%c%c %s\n", 155 (findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? L'A' : L' ', 156 (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? L'S' : L' ', 157 (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? L'H' : L' ', 158 (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? L'R' : L' ', 159 szFullName); 160 } 161 while(FindNextFileW(hFind, &findData)); 162 FindClose(hFind); 163 164 return 0; 165 } 166 167 168 static 169 INT 170 ChangeAttribute( 171 LPWSTR pszPath, 172 LPWSTR pszFile, 173 DWORD dwMask, 174 DWORD dwAttrib, 175 BOOL bRecurse, 176 BOOL bDirectories) 177 { 178 WIN32_FIND_DATAW findData; 179 HANDLE hFind; 180 DWORD dwAttribute; 181 WCHAR szFullName[MAX_PATH]; 182 LPWSTR pszFileName; 183 184 /* prepare full file name buffer */ 185 wcscpy(szFullName, pszPath); 186 pszFileName = szFullName + wcslen(szFullName); 187 188 /* change all subdirectories */ 189 if (bRecurse) 190 { 191 /* append file name */ 192 wcscpy(pszFileName, L"*.*"); 193 194 hFind = FindFirstFileW(szFullName, &findData); 195 if (hFind == INVALID_HANDLE_VALUE) 196 { 197 ErrorMessage(GetLastError(), pszFile); 198 return 1; 199 } 200 201 do 202 { 203 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 204 { 205 if (!wcscmp(findData.cFileName, L".") || 206 !wcscmp(findData.cFileName, L"..")) 207 continue; 208 209 wcscpy(pszFileName, findData.cFileName); 210 wcscat(pszFileName, L"\\"); 211 212 ChangeAttribute(szFullName, pszFile, dwMask, 213 dwAttrib, bRecurse, bDirectories); 214 } 215 } 216 while (FindNextFileW(hFind, &findData)); 217 FindClose(hFind); 218 } 219 220 /* append file name */ 221 wcscpy(pszFileName, pszFile); 222 223 hFind = FindFirstFileW(szFullName, &findData); 224 if (hFind == INVALID_HANDLE_VALUE) 225 { 226 ErrorMessage(GetLastError(), pszFile); 227 return 1; 228 } 229 230 do 231 { 232 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 233 continue; 234 235 wcscpy(pszFileName, findData.cFileName); 236 237 dwAttribute = GetFileAttributes (szFullName); 238 239 if (dwAttribute != 0xFFFFFFFF) 240 { 241 dwAttribute = (dwAttribute & ~dwMask) | dwAttrib; 242 SetFileAttributes(szFullName, dwAttribute); 243 } 244 } 245 while (FindNextFileW(hFind, &findData)); 246 FindClose(hFind); 247 248 return 0; 249 } 250 251 252 int wmain(int argc, WCHAR *argv[]) 253 { 254 INT i; 255 WCHAR szPath[MAX_PATH]; 256 WCHAR szFileName [MAX_PATH]; 257 BOOL bRecurse = FALSE; 258 BOOL bDirectories = FALSE; 259 DWORD dwAttrib = 0; 260 DWORD dwMask = 0; 261 262 /* Initialize the Console Standard Streams */ 263 ConInitStdStreams(); 264 265 /* Print help */ 266 if (argc > 1 && wcscmp(argv[1], L"/?") == 0) 267 { 268 ConResPuts(StdOut, STRING_ATTRIB_HELP); 269 return 0; 270 } 271 272 /* check for options */ 273 for (i = 1; i < argc; i++) 274 { 275 if (wcsicmp(argv[i], L"/s") == 0) 276 bRecurse = TRUE; 277 else if (wcsicmp(argv[i], L"/d") == 0) 278 bDirectories = TRUE; 279 } 280 281 /* create attributes and mask */ 282 for (i = 1; i < argc; i++) 283 { 284 if (*argv[i] == L'+') 285 { 286 if (wcslen(argv[i]) != 2) 287 { 288 ConResPrintf(StdErr, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 289 return -1; 290 } 291 292 switch (towupper(argv[i][1])) 293 { 294 case L'A': 295 dwMask |= FILE_ATTRIBUTE_ARCHIVE; 296 dwAttrib |= FILE_ATTRIBUTE_ARCHIVE; 297 break; 298 299 case L'H': 300 dwMask |= FILE_ATTRIBUTE_HIDDEN; 301 dwAttrib |= FILE_ATTRIBUTE_HIDDEN; 302 break; 303 304 case L'R': 305 dwMask |= FILE_ATTRIBUTE_READONLY; 306 dwAttrib |= FILE_ATTRIBUTE_READONLY; 307 break; 308 309 case L'S': 310 dwMask |= FILE_ATTRIBUTE_SYSTEM; 311 dwAttrib |= FILE_ATTRIBUTE_SYSTEM; 312 break; 313 314 default: 315 ConResPrintf(StdErr, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 316 return -1; 317 } 318 } 319 else if (*argv[i] == L'-') 320 { 321 if (wcslen(argv[i]) != 2) 322 { 323 ConResPrintf(StdErr, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 324 return -1; 325 } 326 327 switch (towupper(argv[i][1])) 328 { 329 case L'A': 330 dwMask |= FILE_ATTRIBUTE_ARCHIVE; 331 dwAttrib &= ~FILE_ATTRIBUTE_ARCHIVE; 332 break; 333 334 case L'H': 335 dwMask |= FILE_ATTRIBUTE_HIDDEN; 336 dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN; 337 break; 338 339 case L'R': 340 dwMask |= FILE_ATTRIBUTE_READONLY; 341 dwAttrib &= ~FILE_ATTRIBUTE_READONLY; 342 break; 343 344 case L'S': 345 dwMask |= FILE_ATTRIBUTE_SYSTEM; 346 dwAttrib &= ~FILE_ATTRIBUTE_SYSTEM; 347 break; 348 349 default: 350 ConResPrintf(StdErr, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 351 return -1; 352 } 353 } 354 } 355 356 if (argc == 1) 357 { 358 DWORD len; 359 360 len = GetCurrentDirectory(MAX_PATH, szPath); 361 if (szPath[len-1] != L'\\') 362 { 363 szPath[len] = L'\\'; 364 szPath[len + 1] = UNICODE_NULL; 365 } 366 wcscpy(szFileName, L"*.*"); 367 PrintAttribute(szPath, szFileName, bRecurse); 368 return 0; 369 } 370 371 /* get full file name */ 372 for (i = 1; i < argc; i++) 373 { 374 if ((*argv[i] != L'+') && (*argv[i] != L'-') && (*argv[i] != L'/')) 375 { 376 LPWSTR p; 377 378 GetFullPathName(argv[i], MAX_PATH, szPath, NULL); 379 p = wcsrchr(szPath, L'\\') + 1; 380 wcscpy(szFileName, p); 381 *p = L'\0'; 382 383 if (dwMask == 0) 384 PrintAttribute(szPath, szFileName, bRecurse); 385 else 386 ChangeAttribute(szPath, szFileName, dwMask, 387 dwAttrib, bRecurse, bDirectories); 388 } 389 } 390 391 return 0; 392 } 393