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(StdOut, 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(StdOut, L"%s -- %s\n", szMsg, szMessage); 85 else 86 ConPrintf(StdOut, L"%s\n", szMsg); 87 } 88 89 /* Returns TRUE if anything is printed, FALSE otherwise */ 90 static 91 BOOL 92 PrintAttribute( 93 LPWSTR pszPath, 94 LPWSTR pszFile, 95 BOOL bRecurse, 96 BOOL bDirectories) 97 { 98 WIN32_FIND_DATAW findData; 99 HANDLE hFind; 100 WCHAR szFullName[MAX_PATH]; 101 LPWSTR pszFileName; 102 BOOL bFound = FALSE; 103 BOOL bIsDir; 104 BOOL bExactMatch; 105 DWORD Error; 106 107 /* prepare full file name buffer */ 108 wcscpy(szFullName, pszPath); 109 pszFileName = szFullName + wcslen(szFullName); 110 111 /* display all subdirectories */ 112 if (bRecurse) 113 { 114 /* append *.* */ 115 wcscpy(pszFileName, L"*.*"); 116 117 hFind = FindFirstFileW(szFullName, &findData); 118 if (hFind == INVALID_HANDLE_VALUE) 119 { 120 Error = GetLastError(); 121 if ((Error != ERROR_DIRECTORY) && (Error != ERROR_SHARING_VIOLATION) 122 && (Error != ERROR_FILE_NOT_FOUND)) 123 { 124 ErrorMessage(Error, pszFile); 125 } 126 return FALSE; 127 } 128 129 do 130 { 131 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 132 continue; 133 134 if (!wcscmp(findData.cFileName, L".") || 135 !wcscmp(findData.cFileName, L"..")) 136 { 137 continue; 138 } 139 140 wcscpy(pszFileName, findData.cFileName); 141 wcscat(pszFileName, L"\\"); 142 bFound |= PrintAttribute(szFullName, pszFile, bRecurse, bDirectories); 143 } 144 while (FindNextFileW(hFind, &findData)); 145 FindClose(hFind); 146 } 147 148 /* append file name */ 149 wcscpy(pszFileName, pszFile); 150 151 /* search current directory */ 152 hFind = FindFirstFileW(szFullName, &findData); 153 if (hFind == INVALID_HANDLE_VALUE) 154 { 155 return bFound; 156 } 157 158 do 159 { 160 bIsDir = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 161 bExactMatch = wcsicmp(findData.cFileName, pszFile) == 0; 162 163 if (bIsDir && !bDirectories && !bExactMatch) 164 continue; 165 166 if (!wcscmp(findData.cFileName, L".") || 167 !wcscmp(findData.cFileName, L"..")) 168 { 169 continue; 170 } 171 172 wcscpy(pszFileName, findData.cFileName); 173 174 ConPrintf(StdOut, 175 L"%c %c%c%c %s\n", 176 (findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? L'A' : L' ', 177 (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? L'S' : L' ', 178 (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? L'H' : L' ', 179 (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? L'R' : L' ', 180 szFullName); 181 bFound = TRUE; 182 } 183 while (FindNextFileW(hFind, &findData)); 184 FindClose(hFind); 185 186 return bFound; 187 } 188 189 190 /* Returns TRUE if anything changed, FALSE otherwise */ 191 static 192 BOOL 193 ChangeAttribute( 194 LPWSTR pszPath, 195 LPWSTR pszFile, 196 BOOL bRecurse, 197 BOOL bDirectories, 198 DWORD dwMask, 199 DWORD dwAttrib) 200 { 201 WIN32_FIND_DATAW findData; 202 HANDLE hFind; 203 WCHAR szFullName[MAX_PATH]; 204 LPWSTR pszFileName; 205 BOOL bFound = FALSE; 206 BOOL bIsDir; 207 BOOL bExactMatch; 208 DWORD dwAttribute; 209 DWORD Error; 210 211 /* prepare full file name buffer */ 212 wcscpy(szFullName, pszPath); 213 pszFileName = szFullName + wcslen(szFullName); 214 215 /* display all subdirectories */ 216 if (bRecurse) 217 { 218 /* append *.* */ 219 wcscpy(pszFileName, L"*.*"); 220 221 hFind = FindFirstFileW(szFullName, &findData); 222 if (hFind == INVALID_HANDLE_VALUE) 223 { 224 Error = GetLastError(); 225 if ((Error != ERROR_DIRECTORY) && (Error != ERROR_SHARING_VIOLATION) 226 && (Error != ERROR_FILE_NOT_FOUND)) 227 { 228 ErrorMessage(Error, pszFile); 229 } 230 return FALSE; 231 } 232 233 do 234 { 235 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 236 continue; 237 238 if (!wcscmp(findData.cFileName, L".") || 239 !wcscmp(findData.cFileName, L"..")) 240 { 241 continue; 242 } 243 244 wcscpy(pszFileName, findData.cFileName); 245 wcscat(pszFileName, L"\\"); 246 bFound |= ChangeAttribute(szFullName, pszFile, bRecurse, bDirectories, 247 dwMask, dwAttrib); 248 } 249 while (FindNextFileW(hFind, &findData)); 250 FindClose(hFind); 251 } 252 253 /* append file name */ 254 wcscpy(pszFileName, pszFile); 255 256 /* search current directory */ 257 hFind = FindFirstFileW(szFullName, &findData); 258 if (hFind == INVALID_HANDLE_VALUE) 259 { 260 return bFound; 261 } 262 263 do 264 { 265 bIsDir = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 266 bExactMatch = wcsicmp(findData.cFileName, pszFile) == 0; 267 268 if (bIsDir && !bDirectories && !bExactMatch) 269 continue; 270 271 if (!wcscmp(findData.cFileName, L".") || 272 !wcscmp(findData.cFileName, L"..")) 273 { 274 continue; 275 } 276 277 if (bRecurse && bIsDir && !bDirectories) 278 continue; 279 280 wcscpy(pszFileName, findData.cFileName); 281 282 dwAttribute = (findData.dwFileAttributes & ~dwMask) | dwAttrib; 283 284 SetFileAttributes(szFullName, dwAttribute); 285 bFound = TRUE; 286 } 287 while (FindNextFileW(hFind, &findData)); 288 FindClose(hFind); 289 290 return bFound; 291 } 292 293 int wmain(int argc, WCHAR *argv[]) 294 { 295 INT i; 296 WCHAR szPath[MAX_PATH] = L""; // For case we only use 'attrib +h /s' there is no szPath 297 WCHAR szFileName [MAX_PATH]; 298 BOOL bRecurse = FALSE; 299 BOOL bDirectories = FALSE; 300 DWORD dwAttrib = 0; 301 DWORD dwMask = 0; 302 LPWSTR p; 303 304 /* Initialize the Console Standard Streams */ 305 ConInitStdStreams(); 306 307 /* Print help */ 308 if (argc > 1 && wcscmp(argv[1], L"/?") == 0) 309 { 310 ConResPuts(StdOut, STRING_ATTRIB_HELP); 311 return 0; 312 } 313 314 /* check for options */ 315 for (i = 1; i < argc; i++) 316 { 317 if (wcsicmp(argv[i], L"/s") == 0) 318 bRecurse = TRUE; 319 else if (wcsicmp(argv[i], L"/d") == 0) 320 bDirectories = TRUE; 321 } 322 323 /* create attributes and mask */ 324 for (i = 1; i < argc; i++) 325 { 326 if (*argv[i] == L'+') 327 { 328 if (wcslen(argv[i]) != 2) 329 { 330 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 331 return -1; 332 } 333 334 switch (towupper(argv[i][1])) 335 { 336 case L'A': 337 dwMask |= FILE_ATTRIBUTE_ARCHIVE; 338 dwAttrib |= FILE_ATTRIBUTE_ARCHIVE; 339 break; 340 341 case L'H': 342 dwMask |= FILE_ATTRIBUTE_HIDDEN; 343 dwAttrib |= FILE_ATTRIBUTE_HIDDEN; 344 break; 345 346 case L'R': 347 dwMask |= FILE_ATTRIBUTE_READONLY; 348 dwAttrib |= FILE_ATTRIBUTE_READONLY; 349 break; 350 351 case L'S': 352 dwMask |= FILE_ATTRIBUTE_SYSTEM; 353 dwAttrib |= FILE_ATTRIBUTE_SYSTEM; 354 break; 355 356 default: 357 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 358 return -1; 359 } 360 } 361 else if (*argv[i] == L'-') 362 { 363 if (wcslen(argv[i]) != 2) 364 { 365 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 366 return -1; 367 } 368 369 switch (towupper(argv[i][1])) 370 { 371 case L'A': 372 dwMask |= FILE_ATTRIBUTE_ARCHIVE; 373 dwAttrib &= ~FILE_ATTRIBUTE_ARCHIVE; 374 break; 375 376 case L'H': 377 dwMask |= FILE_ATTRIBUTE_HIDDEN; 378 dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN; 379 break; 380 381 case L'R': 382 dwMask |= FILE_ATTRIBUTE_READONLY; 383 dwAttrib &= ~FILE_ATTRIBUTE_READONLY; 384 break; 385 386 case L'S': 387 dwMask |= FILE_ATTRIBUTE_SYSTEM; 388 dwAttrib &= ~FILE_ATTRIBUTE_SYSTEM; 389 break; 390 391 default: 392 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 393 return -1; 394 } 395 } 396 } 397 398 if (argc == 1) 399 { 400 DWORD len; 401 402 len = GetCurrentDirectory(MAX_PATH, szPath); 403 if (szPath[len-1] != L'\\') 404 { 405 szPath[len] = L'\\'; 406 szPath[len + 1] = UNICODE_NULL; 407 } 408 wcscpy(szFileName, L"*.*"); 409 PrintAttribute(szPath, szFileName, bRecurse, bDirectories); 410 return 0; 411 } 412 413 /* get full file name */ 414 for (i = 1; i < argc; i++) 415 { 416 if (*argv[i] == L'+' || *argv[i] == L'-' || *argv[i] == L'/') 417 continue; 418 419 GetFullPathNameW(argv[i], MAX_PATH, szPath, &p); 420 wcscpy(szFileName, p); 421 *p = 0; 422 423 if (dwMask == 0) 424 { 425 if (!PrintAttribute(szPath, szFileName, bRecurse, bDirectories)) 426 { 427 ConResPrintf(StdOut, STRING_FILE_NOT_FOUND, argv[i]); 428 } 429 } 430 else if (!ChangeAttribute(szPath, szFileName, bRecurse, bDirectories, dwMask, dwAttrib)) 431 { 432 ConResPrintf(StdOut, STRING_FILE_NOT_FOUND, argv[i]); 433 } 434 } 435 436 // Code below handles the special case of 'attrib +h /s' and similar 437 438 if (bRecurse && dwMask && (wcscmp(szPath, L"") == 0)) 439 { 440 DWORD len; 441 442 len = GetCurrentDirectory(MAX_PATH, szPath); 443 if (szPath[len-1] != L'\\') 444 { 445 szPath[len] = L'\\'; 446 szPath[len + 1] = UNICODE_NULL; 447 } 448 wcscpy(szFileName, L"*.*"); 449 if (!ChangeAttribute(szPath, szFileName, bRecurse, bDirectories, dwMask, dwAttrib)) 450 ConResPrintf(StdOut, STRING_FILE_NOT_FOUND, szFileName); 451 } 452 453 return 0; 454 } 455