Skip to content

Instantly share code, notes, and snippets.

@chergert
Created October 17, 2008 04:48
Show Gist options
  • Save chergert/17354 to your computer and use it in GitHub Desktop.
Save chergert/17354 to your computer and use it in GitHub Desktop.
#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