copyrights in native sources
[fedora-idea.git] / native / fileWatcher / fileWatcher3.cpp
blob97c32fdbfafb9a121b1b15d489dab62a3fb8a18c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "stdafx.h"
19 struct WatchRootInfo {
20 char driveLetter;
21 HANDLE hThread;
22 HANDLE hStopEvent;
23 bool bInitialized;
24 bool bUsed;
25 bool bFailed;
28 const int ROOT_COUNT = 26;
30 WatchRootInfo watchRootInfos[ROOT_COUNT];
32 CRITICAL_SECTION csOutput;
34 bool IsNetworkDrive(const char *name)
36 const int BUF_SIZE = 1024;
37 char buffer[BUF_SIZE];
38 UNIVERSAL_NAME_INFO* uni = (UNIVERSAL_NAME_INFO*) buffer;
39 DWORD size = BUF_SIZE;
41 DWORD result = WNetGetUniversalNameA(
42 name, // path for network resource
43 UNIVERSAL_NAME_INFO_LEVEL, // level of information
44 buffer, // name buffer
45 &size // size of buffer
48 return result == NO_ERROR;
51 bool IsSubstedDrive(const char* name)
53 char deviceName[3] = {name[0], name[1], 0};
54 const int BUF_SIZE = 1024;
55 char targetPath[BUF_SIZE];
57 DWORD result = QueryDosDeviceA(deviceName, targetPath, BUF_SIZE);
58 if (result == 0) {
59 return false;
61 else {
62 bool result = (targetPath[0] == '\\' && targetPath[1] == '?' && targetPath[2] == '?');
63 return result;
67 bool IsUnwatchableFS(const char *path)
69 char volumeName[MAX_PATH];
70 char fsName[MAX_PATH];
71 DWORD fsFlags;
72 DWORD maxComponentLength;
73 SetErrorMode(SEM_FAILCRITICALERRORS);
74 if (!GetVolumeInformationA(path, volumeName, MAX_PATH-1, NULL, &maxComponentLength, &fsFlags, fsName, MAX_PATH-1))
75 return false;
76 if (strcmp(fsName, "NTFS") && strcmp(fsName, "FAT") && strcmp(fsName, "FAT32"))
77 return true;
79 if (!strcmp(fsName, "NTFS") && maxComponentLength != 255 && !(fsFlags & FILE_SUPPORTS_REPARSE_POINTS))
81 // SAMBA reports itself as NTFS
82 return true;
85 return false;
88 bool IsWatchable(const char *path)
90 if (IsNetworkDrive(path))
91 return false;
92 if (IsSubstedDrive(path))
93 return false;
94 if (IsUnwatchableFS(path))
95 return false;
96 return true;
99 const int BUFSIZE = 1024;
101 void PrintMountPointsForVolume(HANDLE hVol, const char* volumePath, char *Buf)
103 HANDLE hPt; // handle for mount point scan
104 char Path[BUFSIZE]; // string buffer for mount points
105 DWORD dwSysFlags; // flags that describe the file system
106 char FileSysNameBuf[BUFSIZE];
108 // Is this volume NTFS?
109 GetVolumeInformationA(Buf, NULL, 0, NULL, NULL, &dwSysFlags, FileSysNameBuf, BUFSIZE);
111 // Detect support for reparse points, and therefore for volume
112 // mount points, which are implemented using reparse points.
114 if (! (dwSysFlags & FILE_SUPPORTS_REPARSE_POINTS)) {
115 return;
118 // Start processing mount points on this volume.
119 hPt = FindFirstVolumeMountPointA(
120 Buf, // root path of volume to be scanned
121 Path, // pointer to output string
122 BUFSIZE // size of output buffer
125 // Shall we error out?
126 if (hPt == INVALID_HANDLE_VALUE) {
127 return;
130 // Process the volume mount point.
131 do {
132 printf("%s%s\n", volumePath, Path);
133 } while (FindNextVolumeMountPointA(hPt, Path, BUFSIZE));
135 FindVolumeMountPointClose(hPt);
138 void PrintMountPoints(const char *path)
140 char volumeUniqueName[128];
141 BOOL res = GetVolumeNameForVolumeMountPointA(path, volumeUniqueName, 128);
142 if (!res) {
143 return;
146 char buf[BUFSIZE]; // buffer for unique volume identifiers
147 HANDLE hVol; // handle for the volume scan
149 // Open a scan for volumes.
150 hVol = FindFirstVolumeA(buf, BUFSIZE );
152 // Shall we error out?
153 if (hVol == INVALID_HANDLE_VALUE) {
154 return;
157 do {
158 if (!strcmp(buf, volumeUniqueName)) {
159 PrintMountPointsForVolume(hVol, path, buf);
161 } while (FindNextVolumeA(hVol, buf, BUFSIZE));
163 FindVolumeClose(hVol);
166 void PrintChangeInfo(char *rootPath, FILE_NOTIFY_INFORMATION *info)
168 char FileNameBuffer[_MAX_PATH];
169 int converted = WideCharToMultiByte(CP_ACP, 0, info->FileName, info->FileNameLength/sizeof(WCHAR), FileNameBuffer, _MAX_PATH-1, NULL, NULL);
170 FileNameBuffer[converted] = '\0';
171 char *command;
172 if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_OLD_NAME)
174 command = "CREATE";
176 else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME)
178 command = "DELETE";
180 else if (info->Action == FILE_ACTION_MODIFIED)
182 command = "CHANGE";
184 else
186 return; // unknown command
189 EnterCriticalSection(&csOutput);
190 puts(command);
191 printf("%s", rootPath);
192 puts(FileNameBuffer);
193 fflush(stdout);
194 LeaveCriticalSection(&csOutput);
197 DWORD WINAPI WatcherThread(void *param)
199 WatchRootInfo *info = (WatchRootInfo *) param;
201 OVERLAPPED overlapped;
202 memset(&overlapped, 0, sizeof(overlapped));
203 overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
205 char rootPath[8];
206 sprintf_s(rootPath, 8, "%c:\\", info->driveLetter);
207 HANDLE hRootDir = CreateFileA(rootPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
208 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
210 int buffer_size = 10240;
211 char *buffer = new char[buffer_size];
213 HANDLE handles [2];
214 handles [0] = info->hStopEvent;
215 handles [1] = overlapped.hEvent;
216 while(true)
218 int rcDir = ReadDirectoryChangesW(hRootDir, buffer, buffer_size, TRUE,
219 FILE_NOTIFY_CHANGE_FILE_NAME |
220 FILE_NOTIFY_CHANGE_DIR_NAME |
221 FILE_NOTIFY_CHANGE_ATTRIBUTES |
222 FILE_NOTIFY_CHANGE_SIZE |
223 FILE_NOTIFY_CHANGE_LAST_WRITE,
224 NULL,
225 &overlapped,
226 NULL);
227 if (rcDir == 0)
229 info->bFailed = true;
230 break;
233 int rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
234 if (rc == WAIT_OBJECT_0)
236 break;
238 if (rc == WAIT_OBJECT_0+1)
240 FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *) buffer;
241 while(true)
243 PrintChangeInfo(rootPath, info);
244 if (!info->NextEntryOffset)
245 break;
246 info = (FILE_NOTIFY_INFORMATION *) ((char *) info + info->NextEntryOffset);
250 CloseHandle(overlapped.hEvent);
251 CloseHandle(hRootDir);
252 delete[] buffer;
253 return 0;
256 void MarkAllRootsUnused()
258 for(int i=0; i<ROOT_COUNT; i++)
260 watchRootInfos [i].bUsed = false;
264 void StartRoot(WatchRootInfo *info)
266 info->hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
267 info->hThread = CreateThread(NULL, 0, &WatcherThread, info, 0, NULL);
268 info->bInitialized = true;
271 void StopRoot(WatchRootInfo *info)
273 SetEvent(info->hStopEvent);
274 WaitForSingleObject(info->hThread, INFINITE);
275 CloseHandle(info->hThread);
276 CloseHandle(info->hStopEvent);
277 info->bInitialized = false;
280 void UpdateRoots()
282 char infoBuffer [256];
283 strcpy_s(infoBuffer, "UNWATCHEABLE\n");
284 for(int i=0; i<ROOT_COUNT; i++)
286 if (watchRootInfos [i].bInitialized && (!watchRootInfos [i].bUsed || watchRootInfos [i].bFailed))
288 StopRoot(&watchRootInfos [i]);
289 watchRootInfos [i].bFailed = false;
291 if (watchRootInfos [i].bUsed)
293 char rootPath[8];
294 sprintf_s(rootPath, 8, "%c:\\", watchRootInfos [i].driveLetter);
295 if (!IsWatchable(rootPath))
297 strcat_s(infoBuffer, rootPath);
298 strcat_s(infoBuffer, "\n");
299 continue;
301 if (!watchRootInfos [i].bInitialized)
303 StartRoot(&watchRootInfos [i]);
307 EnterCriticalSection(&csOutput);
308 fprintf(stdout, "%s", infoBuffer);
309 for(int i=0; i<ROOT_COUNT; i++)
311 if (watchRootInfos [i].bUsed)
313 char rootPath[8];
314 sprintf_s(rootPath, 8, "%c:\\", watchRootInfos [i].driveLetter);
315 PrintMountPoints(rootPath);
318 puts("#");
319 fflush(stdout);
320 LeaveCriticalSection(&csOutput);
323 int _tmain(int argc, _TCHAR* argv[])
325 InitializeCriticalSection(&csOutput);
327 for(int i=0; i<26; i++)
329 watchRootInfos [i].driveLetter = 'A' + i;
330 watchRootInfos [i].bInitialized = false;
331 watchRootInfos [i].bUsed = false;
334 char buffer[8192];
335 while(true)
337 if (!gets_s(buffer, sizeof(buffer)-1))
338 break;
340 if (!strcmp(buffer, "ROOTS"))
342 MarkAllRootsUnused();
343 bool failed = false;
344 while(true)
346 if (!gets_s(buffer, sizeof(buffer)-1))
348 failed = true;
349 break;
351 if (buffer [0] == '#')
352 break;
353 int driveLetterPos = 0;
354 _strupr_s(buffer, sizeof(buffer)-1);
355 char driveLetter = buffer[0];
356 if (driveLetter == '|')
357 driveLetter = buffer[1];
358 if (driveLetter >= 'A' && driveLetter <= 'Z')
360 watchRootInfos [driveLetter-'A'].bUsed = true;
363 if (failed)
364 break;
366 UpdateRoots();
368 if (!strcmp(buffer, "EXIT"))
369 break;
372 MarkAllRootsUnused();
373 UpdateRoots();
375 DeleteCriticalSection(&csOutput);