Created
October 17, 2008 04:48
-
-
Save chergert/17354 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
#include <shlwapi.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include "tail.h" | |
#define MAX_LINE_LENGTH 16384 | |
#define MAX_READ_LENGTH 4096 | |
#define NOTIFY_BUF_LENGTH 4096 | |
BOOL | |
EndOfFile (HANDLE *hFile) | |
{ | |
DWORD cur, eof; | |
eof = GetFileSize (hFile, NULL); | |
cur = SetFilePointer (hFile, 0, NULL, FILE_CURRENT); | |
if (cur < eof) | |
return FALSE; | |
return TRUE; | |
} | |
char* | |
GetAbsDirectory (const char *file) | |
{ | |
char buffer[MAX_PATH], *e, *d; | |
DWORD s; | |
PathSearchAndQualifyA (file, buffer, MAX_PATH); | |
e = strrchr (buffer, '\\'); | |
if (e != NULL) | |
{ | |
s = (DWORD) e - ((DWORD) &buffer); | |
d = malloc (s); | |
return memcpy (d, &buffer, s); | |
} | |
return NULL; | |
} | |
TailStatus | |
tail (const char *file, TailLineReceived callback, const char *delimeter) | |
{ | |
HANDLE hFile, hDir; | |
char *work, *data, *last, *idx, *nbuf, *dir = NULL; | |
char shortName[MAX_PATH], nFileName[MAX_PATH]; | |
int pos, size; | |
size_t n_read; | |
SECURITY_ATTRIBUTES securityAttributes; | |
FILE_NOTIFY_INFORMATION *fNotify; | |
// | |
// Check that we can find the file by finding its directory | |
// | |
dir = GetAbsDirectory (file); | |
if (dir == NULL) | |
return TAIL_STATUS_ERROR; | |
hDir = CreateFile (dir, | |
FILE_LIST_DIRECTORY, | |
FILE_SHARE_READ | FILE_SHARE_WRITE, | |
NULL, | |
OPEN_EXISTING, | |
FILE_FLAG_BACKUP_SEMANTICS, | |
NULL); | |
free (dir); | |
if ((int) hDir < 0) | |
return TAIL_STATUS_ERROR; | |
// | |
// Retreive the shortname for use in events | |
// | |
GetShortPathName (file, shortName, MAX_PATH); | |
// | |
// Open the file for reading | |
// | |
hFile = CreateFile (file, | |
GENERIC_READ, | |
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
&securityAttributes, | |
OPEN_EXISTING, | |
FILE_ATTRIBUTE_NORMAL, | |
NULL); | |
if ((int) hFile < 0) | |
return TAIL_STATUS_ERROR; | |
// | |
// Seek to the end of the file | |
// | |
SetFilePointer (hFile, 0, NULL, FILE_END); | |
// | |
// Initialize our buffers on the heap | |
// | |
pos = 0; | |
data = malloc (MAX_LINE_LENGTH); | |
work = malloc (MAX_READ_LENGTH + 1); | |
nbuf = malloc (NOTIFY_BUF_LENGTH); | |
memset (data, 0, MAX_LINE_LENGTH); | |
memset (work, 0, MAX_READ_LENGTH + 1); | |
while (TRUE) | |
{ | |
ReadDirectoryChangesW (hDir, | |
nbuf, | |
NOTIFY_BUF_LENGTH, | |
FALSE, | |
FILE_NOTIFY_CHANGE_FILE_NAME | |
| FILE_NOTIFY_CHANGE_SIZE, | |
NULL, NULL, NULL); | |
fNotify = (FILE_NOTIFY_INFORMATION*) nbuf; | |
do | |
{ | |
// | |
// Get the notification file as a char* | |
// | |
wcstombs (nFileName, fNotify->FileName, MAX_PATH); | |
// | |
// Move the event to the next position since we do not need | |
// it anymore on the current iteration. | |
// | |
if (fNotify->NextEntryOffset > 0) | |
fNotify = (FILE_NOTIFY_INFORMATION*) nbuf | |
+ fNotify->NextEntryOffset; | |
else | |
fNotify = NULL; | |
// | |
// If we are not tailing the file in the event, iterate to | |
// the next event. | |
// | |
if (strcmp (shortName, nFileName) != 0) | |
continue; | |
// | |
// Read the file, MAX_READ_BUF bytes at a time and append to the | |
// work buffer. Check the work buffer for lines and send them out. | |
// Any unfinished lines are pushed to the beginning of work and | |
// the next read data is appended. | |
// | |
while (!EndOfFile (hFile)) | |
{ | |
ReadFile (hFile, data, MAX_READ_LENGTH, &n_read, NULL); | |
// | |
// Make sure we aren't going past the max line length | |
// | |
if (n_read + pos > MAX_LINE_LENGTH) | |
{ | |
pos = 0; | |
continue; | |
} | |
// | |
// Copy the read data into work | |
// | |
memcpy ((void*) work + pos, data, n_read); | |
pos += n_read; | |
// | |
// Look for \n in the work area and print them out. | |
// | |
last = work; | |
while (NULL != (idx = strchr (last, '\n'))) | |
{ | |
if (callback != NULL) | |
callback (last, idx - last + 1); | |
last = idx + 1; | |
} | |
// | |
// Move our remaining data to the beginning of the buffer | |
// | |
if (last < (work + pos)) | |
{ | |
// | |
// Move the unfinished lines to the beginning of the buffer | |
// and reset the end-of-buffer marker to the end of the | |
// unifinished data (well the count anyway). Also zero out | |
// the extra memory. | |
// | |
size = pos - (last - work); | |
memmove (work, last, size); | |
pos = size; | |
memset (work + size, 0, MAX_LINE_LENGTH - size); | |
} | |
else | |
{ | |
// | |
// Reset our end-of-buffer marker and zero memory | |
// | |
pos = 0; | |
memset (work, 0, MAX_LINE_LENGTH); | |
} | |
} | |
} | |
while (fNotify != NULL); | |
} | |
// | |
// Free our malloc'd buffers | |
// | |
free (data); | |
free (work); | |
free (nbuf); | |
// | |
// Close file handles | |
// | |
CloseHandle (hFile); | |
return TAIL_STATUS_NORMAL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment