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 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 wcscpy(pszFileName, findData.cFileName); 149 150 ConPrintf(StdOut, 151 L"%c %c%c%c %s\n", 152 (findData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ? L'A' : L' ', 153 (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? L'S' : L' ', 154 (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? L'H' : L' ', 155 (findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? L'R' : L' ', 156 szFullName); 157 } 158 while(FindNextFileW(hFind, &findData)); 159 FindClose(hFind); 160 161 return 0; 162 } 163 164 165 static 166 BOOL 167 ChangeAttribute( 168 LPWSTR pszPath, 169 LPWSTR pszFile, 170 DWORD dwMask, 171 DWORD dwAttrib, 172 BOOL bRecurse, 173 BOOL bDirectories) 174 { 175 WIN32_FIND_DATAW findData; 176 HANDLE hFind; 177 DWORD dwAttribute; 178 WCHAR szFullName[MAX_PATH]; 179 LPWSTR pszFileName; 180 BOOL bWildcard = (wcschr(pszFile, L'*') || wcschr(pszFile, L'?')); 181 182 /* prepare full file name buffer */ 183 wcscpy(szFullName, pszPath); 184 pszFileName = szFullName + wcslen(szFullName); 185 186 /* append file name */ 187 wcscpy(pszFileName, pszFile); 188 189 hFind = FindFirstFileW(szFullName, &findData); 190 if (hFind == INVALID_HANDLE_VALUE) 191 return FALSE; 192 193 dwAttribute = findData.dwFileAttributes; 194 195 if (!bWildcard) 196 { 197 FindClose(hFind); 198 if (dwAttribute & FILE_ATTRIBUTE_DIRECTORY) 199 { 200 dwAttribute = (dwAttribute & ~dwMask) | dwAttrib; 201 SetFileAttributes(szFullName, dwAttribute); 202 if (bRecurse) 203 { 204 if (bDirectories) 205 { 206 ChangeAttribute(szFullName, L"*", dwMask, dwAttrib, 207 bRecurse, bDirectories); 208 } 209 else 210 { 211 if (!ChangeAttribute(szFullName, L"*", dwMask, dwAttrib, 212 bRecurse, FALSE)) 213 { 214 return FALSE; 215 } 216 } 217 } 218 else 219 { 220 if (!bDirectories) 221 { 222 ChangeAttribute(szFullName, L"*", dwMask, dwAttrib, 223 bRecurse, FALSE); 224 } 225 } 226 return TRUE; 227 } 228 else 229 { 230 dwAttribute = (dwAttribute & ~dwMask) | dwAttrib; 231 SetFileAttributes(szFullName, dwAttribute); 232 return TRUE; 233 } 234 } 235 else 236 { 237 if ((dwAttribute & FILE_ATTRIBUTE_DIRECTORY) && (!bRecurse || !bDirectories)) 238 return FALSE; 239 240 do 241 { 242 dwAttribute = findData.dwFileAttributes; 243 if (dwAttribute & FILE_ATTRIBUTE_DIRECTORY) 244 { 245 if (!bDirectories) 246 continue; 247 248 if (!wcscmp(findData.cFileName, L".") || 249 !wcscmp(findData.cFileName, L"..")) 250 continue; 251 252 wcscpy(pszFileName, findData.cFileName); 253 dwAttribute = (dwAttribute & ~dwMask) | dwAttrib; 254 SetFileAttributes(szFullName, dwAttribute); 255 256 if (bRecurse) 257 { 258 ChangeAttribute(szFullName, findData.cFileName, dwMask, 259 dwAttrib, bRecurse, FALSE); 260 } 261 } 262 else 263 { 264 wcscpy(pszFileName, findData.cFileName); 265 dwAttribute = (dwAttribute & ~dwMask) | dwAttrib; 266 SetFileAttributes(szFullName, dwAttribute); 267 } 268 } while (FindNextFileW(hFind, &findData)); 269 270 FindClose(hFind); 271 } 272 273 return TRUE; 274 } 275 276 int wmain(int argc, WCHAR *argv[]) 277 { 278 INT i; 279 WCHAR szPath[MAX_PATH]; 280 WCHAR szFileName [MAX_PATH]; 281 BOOL bRecurse = FALSE; 282 BOOL bDirectories = FALSE; 283 DWORD dwAttrib = 0; 284 DWORD dwMask = 0; 285 LPWSTR p; 286 287 /* Initialize the Console Standard Streams */ 288 ConInitStdStreams(); 289 290 /* Print help */ 291 if (argc > 1 && wcscmp(argv[1], L"/?") == 0) 292 { 293 ConResPuts(StdOut, STRING_ATTRIB_HELP); 294 return 0; 295 } 296 297 /* check for options */ 298 for (i = 1; i < argc; i++) 299 { 300 if (wcsicmp(argv[i], L"/s") == 0) 301 bRecurse = TRUE; 302 else if (wcsicmp(argv[i], L"/d") == 0) 303 bDirectories = TRUE; 304 } 305 306 /* create attributes and mask */ 307 for (i = 1; i < argc; i++) 308 { 309 if (*argv[i] == L'+') 310 { 311 if (wcslen(argv[i]) != 2) 312 { 313 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 314 return -1; 315 } 316 317 switch (towupper(argv[i][1])) 318 { 319 case L'A': 320 dwMask |= FILE_ATTRIBUTE_ARCHIVE; 321 dwAttrib |= FILE_ATTRIBUTE_ARCHIVE; 322 break; 323 324 case L'H': 325 dwMask |= FILE_ATTRIBUTE_HIDDEN; 326 dwAttrib |= FILE_ATTRIBUTE_HIDDEN; 327 break; 328 329 case L'R': 330 dwMask |= FILE_ATTRIBUTE_READONLY; 331 dwAttrib |= FILE_ATTRIBUTE_READONLY; 332 break; 333 334 case L'S': 335 dwMask |= FILE_ATTRIBUTE_SYSTEM; 336 dwAttrib |= FILE_ATTRIBUTE_SYSTEM; 337 break; 338 339 default: 340 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 341 return -1; 342 } 343 } 344 else if (*argv[i] == L'-') 345 { 346 if (wcslen(argv[i]) != 2) 347 { 348 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 349 return -1; 350 } 351 352 switch (towupper(argv[i][1])) 353 { 354 case L'A': 355 dwMask |= FILE_ATTRIBUTE_ARCHIVE; 356 dwAttrib &= ~FILE_ATTRIBUTE_ARCHIVE; 357 break; 358 359 case L'H': 360 dwMask |= FILE_ATTRIBUTE_HIDDEN; 361 dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN; 362 break; 363 364 case L'R': 365 dwMask |= FILE_ATTRIBUTE_READONLY; 366 dwAttrib &= ~FILE_ATTRIBUTE_READONLY; 367 break; 368 369 case L'S': 370 dwMask |= FILE_ATTRIBUTE_SYSTEM; 371 dwAttrib &= ~FILE_ATTRIBUTE_SYSTEM; 372 break; 373 374 default: 375 ConResPrintf(StdOut, STRING_ERROR_INVALID_PARAM_FORMAT, argv[i]); 376 return -1; 377 } 378 } 379 } 380 381 if (argc == 1) 382 { 383 DWORD len; 384 385 len = GetCurrentDirectory(MAX_PATH, szPath); 386 if (szPath[len-1] != L'\\') 387 { 388 szPath[len] = L'\\'; 389 szPath[len + 1] = UNICODE_NULL; 390 } 391 wcscpy(szFileName, L"*.*"); 392 PrintAttribute(szPath, szFileName, bRecurse); 393 return 0; 394 } 395 396 /* get full file name */ 397 for (i = 1; i < argc; i++) 398 { 399 if (*argv[i] == L'+' || *argv[i] == L'-' || *argv[i] == L'/') 400 continue; 401 402 GetFullPathNameW(argv[i], MAX_PATH, szPath, &p); 403 wcscpy(szFileName, p); 404 *p = 0; 405 406 if (dwMask == 0) 407 { 408 PrintAttribute(szPath, szFileName, bRecurse); 409 } 410 else if (!ChangeAttribute(szPath, szFileName, dwMask, 411 dwAttrib, bRecurse, bDirectories)) 412 { 413 ConResPrintf(StdOut, STRING_FILE_NOT_FOUND, argv[i]); 414 } 415 } 416 417 return 0; 418 } 419