text:
/*
 * CLog.cpp
 *
 *  Created on: 2015年4月22日
 *      Author: chuanjiang.zh@qq.com
 */
//#include "stdafx.h"
#include "CLog.h"
#include <errno.h>
#include <iostream>
#include <memory>
#include <algorithm>
#include <sstream>
#include <fstream>
#include <vector>

#include <time.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef WIN32
#include <windows.h>

#else
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <syslog.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#endif //

#ifndef MAX_PATH
#define MAX_PATH    256
#endif //MAX_PATH

///////////////////////////////////////////////////////////////////
namespace detail
{

enum Constant
{
    kMaxPath = 256,
    kMaxFileSize = 10,
    kMaxFileCount = 2
};

#ifndef va_copy
#define va_copy(x,y) x = y
#endif //

#ifdef WIN32

class CriticalSection
{
public:
    CriticalSection()
    {
        InitializeCriticalSection( &m_cs );
    }

    ~CriticalSection()
    {
        DeleteCriticalSection( &m_cs );
    }

    void lock()
    {
        EnterCriticalSection( &m_cs );
    }

    void unlock()
    {
        LeaveCriticalSection( &m_cs );
    }

    CRITICAL_SECTION &getLock()
    {
        return m_cs;
    }

#if(_WIN32_WINNT >= 0x0400)
    bool trylock()
    {
        return ( TryEnterCriticalSection( &m_cs ) != 0 );
    }
#endif /* _WIN32_WINNT >= 0x0400 */

private:
    CRITICAL_SECTION m_cs;
};

#else

/// cross platform critical section
class CriticalSection
{
public:
    CriticalSection()
    {
        pthread_mutexattr_t attr;
        ::pthread_mutexattr_init(&attr);
        ::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        ::pthread_mutex_init(&m_mutex, &attr);
        ::pthread_mutexattr_destroy(&attr);
    }

    ~CriticalSection()
    {
        ::pthread_mutex_destroy(&m_mutex);
    }

    void lock()
    {
        ::pthread_mutex_lock(&m_mutex);
    }

    void unlock()
    {
        ::pthread_mutex_unlock(&m_mutex);
    }

    bool trylock()
    {
        return (0 == ::pthread_mutex_trylock(&m_mutex));
    }

    pthread_mutex_t &getLock()
    {
        return m_mutex;
    }

private:
    pthread_mutex_t m_mutex;

};

#endif //WIN32

class AutoLock
{
public:
	explicit AutoLock(CriticalSection& cs):
		m_cs(cs)
    {
        m_cs.lock();
    }

    ~AutoLock()
    {
        m_cs.unlock();
    }

private:
    CriticalSection& m_cs;
};

#ifdef WIN32
/*
* Number of micro-seconds between the beginning of the Windows epoch
* (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970).
*
* This assumes all Win32 compilers have 64-bit support.
*/
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) || defined(__WATCOMC__)
  #define DELTA_EPOCH_IN_USEC  11644473600000000Ui64
#else
  #define DELTA_EPOCH_IN_USEC  11644473600000000ULL
#endif
typedef unsigned __int64 u_int64_t;

static u_int64_t filetime_to_unix_epoch (const FILETIME *ft)
{
    u_int64_t res = (u_int64_t) ft->dwHighDateTime << 32;

    res |= ft->dwLowDateTime;
    res /= 10;                   /* from 100 nano-sec periods to usec */
    res -= DELTA_EPOCH_IN_USEC;  /* from Win epoch to Unix epoch */
    return (res);
}

int gettimeofday (struct timeval *tv, void *tz)
{
    FILETIME  ft;
    u_int64_t tim;

    if (!tv) {
        //errno = EINVAL;
        return (-1);
    }
    GetSystemTimeAsFileTime (&ft);
    tim = filetime_to_unix_epoch (&ft);
    tv->tv_sec  = (long) (tim / 1000000L);
    tv->tv_usec = (long) (tim % 1000000L);
    return (0);
}
#else

int gettimeofday (struct timeval *tv, void *tz)
{
    return ::gettimeofday(tv, (struct timezone*)tz);
}
#endif //win32

int getcurtime(struct tm& t, int& ms)
{
	struct timeval tv;
	void* tz = NULL;
	int rc = gettimeofday(&tv, tz);
	time_t sec = tv.tv_sec;

#if defined(_MSC_VER) && (_MSC_VER >= 1400)
    localtime_s(&t, &sec);
#else
    t = *localtime(&sec);
#endif //

    ms = tv.tv_usec/1000;
    return rc;
}

int vsprint(char **buf, size_t size, const char *fmt, va_list ap)
{
	va_list ap_copy;
	int len;

	va_copy(ap_copy, ap);
	len = vsnprintf(*buf, size, fmt, ap_copy);
	va_end(ap_copy);

	if (len < 0)
	{
		// eCos and Windows are not standard-compliant and return -1 when
		// the buffer is too small. Keep allocating larger buffers until we
		// succeed or out of memory.
		*buf = NULL;

		while (len < 0)
		{
			if (*buf)
			{
				free(*buf);
			}
			size *= 2;
			if ((*buf = (char *)malloc(size)) == NULL)
				break;
			va_copy(ap_copy, ap);
			len = vsnprintf(*buf, size, fmt, ap_copy);
			va_end(ap_copy);
		}
	}
	else if (len > (int) size)
	{
		// Standard-compliant code path. Allocate a buffer that is large enough.
		if ((*buf = (char *) malloc(len + 1)) == NULL)
		{
			len = -1;
		}
		else
		{
			va_copy(ap_copy, ap);
			len = vsnprintf(*buf, len + 1, fmt, ap_copy);
			va_end(ap_copy);
		}
	}

	return len;
}

int sprint(char **buf, size_t size, const char *fmt, ...)
{
	va_list argList;
	va_start(argList, fmt);
	return vsprint(buf, size, fmt, argList);
}

///////////////////////////////////////////////////////////////////

/// ini file reader and writer
class IniFile
{
public:
    IniFile();
    ~IniFile();

    explicit IniFile(bool ignoreCase);

    bool load( const char* filename );
    bool save( const char* filename );

    size_t getSectionCount();
    bool existSection( const char* name );
    /// return true if insertion happened
    bool insertSection( const char* name );
    void removeSection( const char* name );

    /// remove comment in the section
    void removeComment( const char* sectionName );
    /// remove all comment in the file
    void removeComment();
    /// insert comment before key
    void setComment( const char* sectionName, const char* keyName,
            const char* comment );
    /// return empty if comment does not exist
    std::string getComment( const char* sectionName, const char* keyName );

    size_t getKeyCount( const char* sectionName );
    bool existKey( const char* sectionName, const char* keyName );
    void removeKey( const char* sectionName, const char* keyName );

    std::string getValue( const char* sectionName, const char* keyName,
            const std::string& defaultValue );
    std::string getValue( const char* sectionName, const char* keyName,
            const char* defaultValue = "");

    void setValue( const char* sectionName, const char* keyName,
            const char* value );
    void setValue( const char* sectionName, const char* keyName,
            const std::string& value );

    template < class T >
    bool queryValue(const char* sectionName, const char* keyName, T& value)
    {
        bool found = false;
        iterator it = findIterator( sectionName );
        if ( it != m_sections.end() )
        {
            IniSection& sec = *it;
            IniEntryIterator pos = sec.findIterator( keyName );
            if ( pos != sec.entryArray.end() )
            {
                std::istringstream iss(pos->value);
                iss >> value;
                found = true;
            }
        }
        return found;
    }

    bool queryValue(const char* sectionName, const char* keyName, bool& value)
    {
        std::string str;
        if (!queryValue(sectionName, keyName, str))
        {
            return false;
        }

        value = (str == "true") || (str == "1");
        return true;
    }

    template < class T >
    T getValue( const char* sectionName, const char* keyName, T defaultValue )
    {
        iterator it = findIterator( sectionName );
        if ( it != m_sections.end() )
        {
            IniSection& sec = *it;
            IniEntryIterator pos = sec.findIterator( keyName );
            if ( pos != sec.entryArray.end() )
            {
                std::istringstream iss( pos->value );
                T t(defaultValue);
                iss >> t;
                return t;
            }
        }
        return defaultValue;
    }

    template < class T >
    void setValue( const char* sectionName, const char* keyName,
                T value )
    {
        IniSection& sec = ensure( sectionName );
        std::ostringstream ss;
        ss << value;
        return sec.set( keyName, ss.str() );
    }

    std::string getFileComment();
    void setFileComment( const std::string& comment );

    void clear();

public:
    class IniEntry
    {
    public:
        std::string key;
        std::string value;
        std::string comment;

        IniEntry()
        {
        }

        explicit IniEntry( const std::string& keyName )
        :key(keyName)
        {
        }

        void clear()
        {
            key.clear();
            value.clear();
            comment.clear();
        }
    };

    typedef std::vector< IniEntry >     IniEntryArray;
    typedef IniEntryArray::iterator     IniEntryIterator;

    class IniSection
    {
    public:
        std::string     name;
        IniEntryArray   entryArray;
        bool m_ignoreCase;

        IniSection();
        IniSection( const std::string& name, bool ignoreCase);

        void set( const char* keyName, const std::string& value );
        bool get(const char* keyName, std::string& value);
        bool get(const char* keyName, int& value);
        bool get(const char* keyName, double& value);
        bool exist(const char* keyName);

        IniEntryIterator findIterator( const char* keyName );
        IniEntry& ensure( const char* keyName );
        void remove( const char* keyName );
        void removeComment();
        void clear();
        bool empty();

    };

    typedef std::vector< IniSection >   IniSectionArray;
    typedef IniSectionArray::iterator   iterator;

    IniSection& getSection(size_t idx);

    IniSection& ensure( const char* name );

    class StringPredicator
    {
    public:
        explicit StringPredicator(bool ignoreCase):
            m_ignoreCase(ignoreCase)
        {

        }

        bool equals(const std::string& left, const std::string& right) const
        {
            if (m_ignoreCase)
            {
                if (left.size() != right.size())
                {
                    return false;
                }

                for (size_t i = 0; i < left.size(); ++ i)
                {
                    if (tolower(left[i]) != tolower(right[i]))
                    {
                        return false;
                    }
                }

                return true;
            }
            else
            {
                return (left == right);
            }
        }

        bool    m_ignoreCase;
    };

private:

    iterator    findIterator( const char* sectionName );

    enum LineFlag
    {
        LINE_UNKNOWN,
        LINE_EMPTY,
        LINE_COMMENT,
        LINE_SECTION,
        LINE_ENTRY
    };

    LineFlag parseLine( const std::string& line );
    char getFirstVisibleChar( const std::string& line );
    std::string parseSectionName( const std::string& line );
    void parseEntry( const std::string& line, IniEntry& entry );

private:
    IniSectionArray     m_sections;
    std::string         m_fileComment;
    bool    m_ignoreCase;

};

using namespace std;

IniFile::IniFile():
    m_ignoreCase(false)
{
}

IniFile::~IniFile()
{
}

IniFile::IniFile(bool ignoreCase):
    m_ignoreCase(ignoreCase)
{

}

bool IniFile::load( const char* filename )
{
    bool done = false;
    std::ifstream ifs;
    ifs.open( filename );
    if ( ifs.is_open() )
    {
        clear();

        /// parse header
        LineFlag flag = LINE_UNKNOWN;
        std::string line;
        while ( std::getline( ifs, line ) )
        {
            flag = parseLine( line );
            if ( flag == LINE_COMMENT )
            {
                m_fileComment += line;
                m_fileComment += "\n";
            }
            else
            {
                break;
            }
        }

        IniSection sec;
        sec.m_ignoreCase = m_ignoreCase;

        IniEntry entry;
        std::string comment;

        do
        {
            switch ( flag )
            {
            case LINE_SECTION:
                {
                    if ( !sec.empty() )
                    {
                        m_sections.push_back( sec );
                        sec.clear();
                    }

                    sec.name = parseSectionName( line );
                }
                break;
            case LINE_COMMENT:
                {
                    comment += line;
                    comment += "\n";
                }
                break;
            case LINE_ENTRY:
                {
                    entry.comment = comment;
                    parseEntry( line, entry );

                    sec.entryArray.push_back( entry );
                    comment.clear();
                    entry.clear();
                }
                break;
            case LINE_EMPTY:
                break;
            default:
                {
                    comment += line;
                    comment += "\n";
                }
                break;
            }

            std::getline( ifs, line );
            flag = parseLine( line );
        } while ( ifs );

        if ( !sec.empty() )
        {
            m_sections.push_back( sec );
        }

        ifs.close();
        done = true;
    }
    return done;
}

bool IniFile::save( const char* filename )
{
    bool success = false;
    std::ofstream ofs;
    ofs.open( filename );
    if ( ofs.is_open() )
    {
        ofs << m_fileComment;

        iterator it = m_sections.begin();
        for ( ; it != m_sections.end(); ++ it )
        {
            IniSection& sec = *it;
            ofs << endl << "[" << sec.name << "]" << endl;

            IniEntryIterator pos = sec.entryArray.begin();
            for (; pos != sec.entryArray.end(); ++ pos )
            {
                if ( !pos->comment.empty() )
                {
                    ofs << pos->comment;
                }
                ofs << pos->key << "=" << pos->value << endl;
            }
        }

        ofs.close();
        success = true;
    }
    return success;
}

size_t IniFile::getSectionCount()
{
    return m_sections.size();
}

bool IniFile::existSection( const char* sectionName )
{
    iterator it = findIterator( sectionName );
    return ( it != m_sections.end() );
}

bool IniFile::insertSection( const char* sectionName )
{
    bool done = false;
    iterator it = findIterator( sectionName );
    if ( it == m_sections.end() )
    {
        m_sections.push_back( IniSection( sectionName, m_ignoreCase ) );
        done = true;
    }
    return done;
}

void IniFile::removeSection( const char* sectionName )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        m_sections.erase( it );
    }
}

void IniFile::removeComment( const char* sectionName )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        it->removeComment();
    }
}

void IniFile::removeComment()
{
    iterator it = m_sections.begin();
    for ( ; it != m_sections.end(); ++ it )
    {
        it->removeComment();
    }
}

void IniFile::setComment( const char* sectionName, const char* keyName, const char* comment )
{
    IniSection& sec = ensure( sectionName );
    IniEntry& entry = sec.ensure( keyName );
    entry.comment = comment;
}

std::string IniFile::getComment( const char* sectionName, const char* keyName )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        IniSection& sec = *it;
        IniEntryIterator pos = sec.findIterator( keyName );
        if ( pos != sec.entryArray.end() )
        {
            return pos->comment;
        }
    }
    return std::string();
}

size_t IniFile::getKeyCount( const char* sectionName )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        return it->entryArray.size();
    }
    return 0;
}

bool IniFile::existKey( const char* sectionName, const char* keyName )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        IniSection& sec = *it;
        return ( sec.findIterator( keyName ) != sec.entryArray.end() );
    }
    return false;
}

void IniFile::removeKey( const char* sectionName, const char* keyName )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        IniSection& sec = *it;
        sec.remove( keyName );
    }
}

std::string IniFile::getValue( const char* sectionName, const char* keyName,
        const std::string& defaultValue )
{
    iterator it = findIterator( sectionName );
    if ( it != m_sections.end() )
    {
        IniSection& sec = *it;
        IniEntryIterator pos = sec.findIterator( keyName );
        if ( pos != sec.entryArray.end() )
        {
            return pos->value;
        }
    }
    return defaultValue;
}

std::string IniFile::getValue( const char* sectionName, const char* keyName,
            const char* defaultValue )
{
    return getValue( sectionName, keyName, std::string(defaultValue) );
}

void IniFile::setValue( const char* sectionName, const char* keyName,
        const char* value )
{
    IniSection& sec = ensure( sectionName );
    sec.set( keyName, value );
}

void IniFile::setValue( const char* sectionName, const char* keyName,
        const std::string& value )
{
    IniSection& sec = ensure( sectionName );
    sec.set( keyName, value );
}

std::string IniFile::getFileComment()
{
    return m_fileComment;
}

void IniFile::setFileComment( const std::string& comment )
{
    m_fileComment = comment;
}

void IniFile::clear()
{
    m_fileComment.clear();
    m_sections.clear();
}

IniFile::IniSection& IniFile::getSection(size_t idx)
{
    assert(idx < m_sections.size());
    return m_sections[idx];
}

IniFile::IniSection& IniFile::ensure( const char* name )
{
    iterator it = findIterator( name );
    if ( it == m_sections.end() )
    {
        m_sections.push_back( IniSection(name, m_ignoreCase) );
        return m_sections.back();
    }
    return *it;
}

IniFile::iterator   IniFile::findIterator( const char* sectionName )
{
    StringPredicator pred(m_ignoreCase);

    iterator it = m_sections.begin();
    for ( ; it != m_sections.end(); ++ it )
    {
        if (pred.equals(it->name, sectionName))
        {
            return it;
        }
    }
    return m_sections.end();
}

IniFile::LineFlag IniFile::parseLine( const std::string& line )
{
    if ( line.empty() )
        return LINE_EMPTY;

    char firstChar = getFirstVisibleChar( line );
    if ( firstChar == ';' || firstChar == '#' )
    {
        return LINE_COMMENT;
    }
    else if ( firstChar == '[' )
    {
        return LINE_SECTION;
    }

    size_t idx = line.find_first_of( '=' );
    if ( idx != std::string::npos )
    {
        return LINE_ENTRY;
    }
    return LINE_UNKNOWN;
}

char IniFile::getFirstVisibleChar( const std::string& line )
{
    for ( size_t i = 0; i < line.size(); ++ i )
    {
        if ( line[i] != ' ' && line[i] != '\t' )
        {
            return line[i];
        }
    }
    return 0;
}

std::string IniFile::parseSectionName( const std::string& line )
{
    size_t start = line.find_first_of( '[' );
    size_t stop = line.find_last_of( ']' );
    if ( stop == std::string::npos )
    {
        return line.substr( start );
    }
    return line.substr( start + 1, stop - start-1 );
}

void IniFile::parseEntry( const std::string& line, IniEntry& entry )
{
    size_t idx = line.find_first_of( '=' );
    entry.key = line.substr( 0, idx );
    if (line[line.size() - 1] == '\r')
    {
        entry.value = line.substr( idx + 1, line.size() -1 - (idx+1) );
    }
    else
    {
        entry.value = line.substr( idx + 1 );
    }
}

IniFile::IniSection::IniSection()
:m_ignoreCase(false)
{
}

IniFile::IniSection::IniSection( const std::string& aName, bool ignoreCase)
:name( aName )
,m_ignoreCase(ignoreCase)
{
}

void IniFile::IniSection::set( const char* keyName, const std::string& value )
{
    IniEntry& entry = ensure( keyName );
    entry.value = value;
}

bool IniFile::IniSection::get(const char* keyName, std::string& value)
{
    bool found = false;
    IniEntryIterator it = findIterator(keyName);
    if (it != entryArray.end())
    {
        value = it->value;
        found = true;
    }
    return found;
}

bool IniFile::IniSection::get(const char* keyName, int& value)
{
    std::string strValue;
    if (!get(keyName, strValue))
    {
        return false;
    }

    std::istringstream iss(strValue);
    iss >> value;

    return true;
}

bool IniFile::IniSection::get(const char* keyName, double& value)
{
    std::string strValue;
    if (!get(keyName, strValue))
    {
        return false;
    }

    std::istringstream iss(strValue);
    iss >> value;

    return true;
}

bool IniFile::IniSection::exist(const char* keyName)
{
    return (findIterator(keyName) != entryArray.end());
}

IniFile::IniEntryIterator IniFile::IniSection::findIterator( const char* keyName )
{
    StringPredicator pred(m_ignoreCase);

    IniEntryIterator it = entryArray.begin();
    for ( ; it != entryArray.end(); ++ it )
    {
        if (pred.equals(it->key, keyName))
        {
            return it;
        }
    }
    return entryArray.end();
}

IniFile::IniEntry& IniFile::IniSection::ensure( const char* keyName )
{
    IniEntryIterator it = findIterator( keyName );
    if ( it == entryArray.end() )
    {
        entryArray.push_back( IniEntry(keyName));
        return entryArray.back();
    }
    return *it;
}

void IniFile::IniSection::remove( const char* keyName )
{
    IniEntryIterator it = findIterator( keyName );
    if ( it != entryArray.end() )
    {
        entryArray.erase( it );
    }
}

void IniFile::IniSection::removeComment()
{
    IniEntryIterator pos = entryArray.begin();
    for ( ; pos != entryArray.end(); ++ pos )
    {
        pos->comment.clear();
    }
}

void IniFile::IniSection::clear()
{
    name.clear();
    entryArray.clear();
}

bool IniFile::IniSection::empty()
{
    return name.empty() && entryArray.empty();
}

///////////////////////////////////////////////////////////////////

int rsplit(const std::string& str, const std::string& sp, std::string& head, std::string& tail)
{
    int ch = -1;
    for (size_t i = str.size(); i > 0; i --)
    {
        size_t idx = i - 1;
        size_t pos = sp.find(str[idx]);
        if (pos != std::string::npos)
        {
            head = str.substr(0, idx);
            tail = str.substr(idx+1);
            ch = sp[pos];
            break;
        }
    }
    return ch;
}

//todo

#ifdef WIN32
bool existDir(const std::string& dirpath)
{
	return (GetFileAttributes(dirpath.c_str()) != 0xFFFFFFFF);
}

bool createDirectory(const std::string& dirpath)
{
    return (::CreateDirectory(dirpath.c_str(), NULL) != FALSE);
}

bool createIfNotExist(const char* path)
{
    BOOL done = TRUE;
    DWORD attr = ::GetFileAttributes(path);
    if (attr == 0xFFFFFFFF)
    {
        done = CreateDirectory(path, NULL);
    }
    return (done != 0);
}

#else

bool existDir(const std::string& path)
{
    struct stat buf;
    memset(&buf, 0, sizeof(buf));
    int ret = stat(path.c_str(), &buf);
    return (ret == 0) && S_ISDIR(buf.st_mode);
}

bool createDirectory(const std::string& path)
{
    int ret = mkdir(path.c_str(), S_IRWXU);
    return (ret == 0 || errno == EEXIST);
}

bool createIfNotExist(const char* path)
{
    if (strlen(path) == 0)
    {
        return true;
    }

    bool done = true;
    struct stat buf;
    memset(&buf, 0, sizeof(buf));
    int ret = stat(path, &buf);
    if ((ret == 0) && S_ISDIR(buf.st_mode))
    {
        done = true;
    }
    else
    {
        ret = mkdir(path, S_IRWXU);
        done = (ret == 0 || errno == EEXIST);
    }
    return done;
}

#endif //

bool createDirectories(const char* path)
{
    size_t length = strlen(path);
    char buffer[MAX_PATH] = {0};
    #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
    strcpy_s(buffer, MAX_PATH, path);
    #else
    strcpy(buffer, path);
    #endif //_MSC_VER

    char sp = '\\';

    for (size_t i = 0; i < length; ++ i)
    {
        if ((buffer[i] == '\\') || (buffer[i] == '/'))
        {
            sp = buffer[i];
            buffer[i] = '\0';
            if (!createIfNotExist(buffer))
            {
                return false;
            }

            buffer[i] = sp;
        }
    }

    if ((path[length - 1] == '\\') || (path[length - 1] == '/'))
    {
        return true;
    }

    return createIfNotExist(buffer);
}

bool ensureParentDir(const std::string& filepath)
{
	std::string sp("/\\");
	std::string dirpath;
	std::string name;

	rsplit(filepath, sp, dirpath, name);
	if (dirpath.empty() || existDir(dirpath))
	{
		return true;
	}
	return createDirectories(dirpath.c_str());
}

void writeCout(int level, const char* data, int length, int flags)
{
	if (flags & CLog::DATETIME)
	{
		printf("%s %s | %s", CLog::getDateTime(), CLog::getName(level), data);
	}
	else if (flags & CLog::TIME)
	{
		printf("%s %s | %s", CLog::getShortTime(), CLog::getName(level), data);
	}
	else
	{
		printf("%s", data);
	}
}

void writeDebug(int level, const char* data, int length, int flags)
{
#ifdef WIN32
	char buffer[1024];
	char* buf = buffer;

	if (flags & CLog::DATETIME)
	{
		detail::sprint(&buf, 1024, "%s %s | %s",
				CLog::getDateTime(), CLog::getName(level), data);
		OutputDebugStringA(buf);
	}
	else if (flags & CLog::TIME)
	{
		detail::sprint(&buf, 1024, "%s %s | %s",
				CLog::getShortTime(), CLog::getName(level), data);
		OutputDebugStringA(buf);
	}
	else
	{
		OutputDebugStringA(data);
	}

	if (buf != buffer)
	{
		free(buf);
	}

#else
    /// pass
#endif //WIN32
}

class LogWriter : public CLog::Writer
{
public:
    WriteLog m_logger;

    explicit LogWriter(WriteLog logger):
        m_logger(logger)
    {
    }

    virtual void write(int level, const char* msg, int length, int flags)
    {
        if (m_logger)
        {
            (m_logger)(level, msg, length, flags);
        }
    }

};

#ifndef WIN32

class SyslogWriter : public CLog::Writer
{
public:
    SyslogWriter()
    {
        openlog(0, LOG_NDELAY | LOG_PID, LOG_LOCAL6);
    }

    ~SyslogWriter()
    {
        closelog();
    }

    struct LevelEntry
    {
        int level;
        int sysLevel;
    };

    static int transform(int level)
    {
        static LevelEntry s_levels[] =
        {
            {CLog::kNone, LOG_DEBUG},
            {CLog::kDebug, LOG_DEBUG},
            {CLog::kInfo, LOG_INFO},
            {CLog::kWarning, LOG_WARNING},
            {CLog::kError, LOG_ERR},
            {CLog::kFatal, LOG_CRIT},
            {CLog::kDebug, LOG_DEBUG},
            {CLog::kDebug, LOG_DEBUG},
        };

        if ((level < 0) || (level >= sizeof(s_levels)))
        {
            return s_levels[0].sysLevel;
        }
        return s_levels[level].sysLevel;
    }

    virtual void write(int level, const char* msg, int length, int flags)
    {
        int sysLevel = transform(level);
        syslog(LOG_LOCAL6 | sysLevel, "%s", msg);
    }

};

#endif //WIN32

class FileLogWriter : public CLog::Writer
{
public:
    explicit FileLogWriter(const char* filepath = ""):
		CLog::Writer(),
	    m_filePath(filepath),
		m_maxFileSize(DEFAULT_MAX_SIZE),
		m_rollCount(DEFAULT_ROLL_COUNT),
		m_file(NULL),
		m_curFileSize()
	{
    	ensureFileDir(m_filePath);

    	openFile();
	}

    ~FileLogWriter()
    {
    	closeFile();
    }

    virtual void write(int level, const char* msg, int length, int flags)
    {
    	if (m_file == NULL)
		{
			if (!openFile())
			{
				return;
			}
		}

		size_t written = 0;
		if (flags & CLog::DATETIME)
		{
			written = fprintf(m_file, "%s %s | %s", CLog::getDateTime(), CLog::getName(level), msg);
		}
		else if (flags & CLog::TIME)
		{
			written = fprintf(m_file, "%s %s | %s", CLog::getShortTime(), CLog::getName(level), msg);
		}
		else
		{
			written = fwrite(msg, 1, length, m_file);
		}

		fflush(m_file);

		m_curFileSize += written;
		if (m_curFileSize >= m_maxFileSize)
		{
			// switch
			closeFile();

			doRollOver();
		}
    }

    bool setFile(const char* filepath)
    {
    	if (filepath == NULL)
		{
			return false;
		}

		closeFile();

		m_filePath = filepath;

		ensureFileDir(m_filePath);

		return openFile();
    }

    void setMaxSize(size_t fileSize)
    {
    	if (fileSize < 1)
    	{
    		return;
    	}
   		m_maxFileSize = fileSize;
    }

    void setRollCount(size_t count)
    {
    	if (count < 1)
    	{
    		return;
    	}
   		m_rollCount = count;
    }

    static const size_t DEFAULT_MAX_SIZE = 10 * 1024 * 1024;
    static const size_t DEFAULT_ROLL_COUNT = 2;

protected:
    bool ensureFileDir(const std::string& filepath)
    {
    	return detail::ensureParentDir(filepath);
    }

    bool openFile()
    {
    	if (m_file == NULL)
		{

	#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
			m_file = _fsopen(m_filePath.c_str(), "a+", _SH_DENYNO);
	#else
			m_file = fopen(m_filePath.c_str(), "a+");
	#endif
			if (m_file)
			{
				fseek(m_file, 0, SEEK_END);
				m_curFileSize = ftell(m_file);
			}
		}
		return (m_file != NULL);
    }

    void closeFile()
    {
    	if (m_file != NULL)
		{
			fclose(m_file);
			m_file = NULL;
		}
    }

    bool isOpen()
    {
    	return (m_file != NULL);
    }

    std::string getBackupFile(const char* path, size_t idx)
    {
        std::ostringstream ss;
        ss << path;
        ss << '.';
        ss << idx;
        return ss.str();
    }

    void doRollOver()
    {
    	if (m_rollCount == 0)
		{
			::remove(m_filePath.c_str());
		}
		else
		{
			// remove last file
			size_t idx = m_rollCount;
			std::string lastFile = getBackupFile(m_filePath.c_str(), idx);
			::remove(lastFile.c_str());

			// rename
			for (idx = m_rollCount - 1; idx > 0; -- idx)
			{
				std::string filepath = getBackupFile(m_filePath.c_str(), idx);
				std::string nextFile = getBackupFile(m_filePath.c_str(), idx + 1);
				::rename(filepath.c_str(), nextFile.c_str());
			}

			std::string nextFile = getBackupFile(m_filePath.c_str(), 1);
			::rename(m_filePath.c_str(), nextFile.c_str());
		}
    }

    std::string m_filePath;
    size_t  m_maxFileSize;
    size_t  m_rollCount;

    FILE*   m_file;
    size_t  m_curFileSize;

};

}

///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////

struct CLogContext
{
    CLog::Level level;
    CLog::Writer*    logger[CLog::MAXLOGGER];
    int flags[CLog::MAXLOGGER];

    char    fileName[detail::kMaxPath];
    int     fileSize;
    int     fileCount;

    detail::CriticalSection	cs;

    CLogContext():
        level(CLog::kNone),
        fileSize(detail::kMaxFileSize),
        fileCount(detail::kMaxFileCount)
    {
        reset();
    }

    void lock()
    {
    	cs.lock();
    }

    void unlock()
    {
    	cs.unlock();
    }

    void reset()
    {
        level = CLog::kNone;
        fileSize = detail::kMaxFileSize;
        fileCount = detail::kMaxFileCount;

        memset(logger, 0, sizeof(logger));
        memset(flags, 0, sizeof(flags));
        memset(fileName, 0, sizeof(fileName));

    }

    void setLogger(CLog::LoggerType type, CLog::Writer* logger, int flags)
    {
        this->logger[type] = logger;
        this->flags[type] = flags;
    }

    void resetLogger()
    {
        for (int i = 0; i < CLog::MAXLOGGER; i ++)
        {
            if (logger[i])
            {
                delete logger[i];
                logger[i] = NULL;
            }
        }
    }
};

static CLogContext s_context;

class AutoLock
{
public:
	AutoLock()
    {
        s_context.lock();
    }

    ~AutoLock()
    {
        s_context.unlock();
    }
};

///////////////////////////////////////////////////////////////////
struct LogLevelEntry
{
    CLog::Level   level;
    const char* name;
};

static LogLevelEntry s_levelEntry[] = {
    { CLog::kNone, "all" },
    { CLog::kDebug, "debug" },
    { CLog::kInfo, "info" },
    { CLog::kWarning, "warning" },
    { CLog::kError, "error" },
    { CLog::kFatal, "fatal" }
};

CLog::CLog()
{

}

CLog::~CLog()
{
}

int CLog::startup(const char* configPath)
{
    return reload(configPath);
}

void CLog::cleanup()
{
    resetLogger();

    s_context.reset();
}

void CLog::log(int level, const char* format, ...)
{
    if (level <= s_context.level)
    {
        return;
    }

    va_list argList;
    va_start(argList, format);

    logv(level, format, argList);

    va_end(argList);
}

void CLog::logv(int level, const char* format, va_list& args)
{
    if (level <= s_context.level)
    {
        return;
    }

    char buffer[1024];
    char* buf = buffer;
    int length = detail::vsprint(&buf, 1024, format, args);

    writeLog(level, buf, length);

    if (buf != buffer)
    {
    	free(buf);
    }
}

void CLog::writeLog(int level, const char* data, int length)
{
    if (level <= s_context.level)
    {
        return;
    }

    detail::AutoLock lock(s_context.cs);

    for (int i = 0; i < MAXLOGGER; i ++)
    {
        if (s_context.logger[i])
        {
        	int flags  = s_context.flags[i];
            s_context.logger[i]->write(level, data, length, flags);
        }
    }
}

void CLog::debug(const char* format, ...)
{
    va_list argList;
    va_start(argList, format);

    logv(kDebug, format, argList);

    va_end(argList);
}

void CLog::info(const char* format, ...)
{
    va_list argList;
    va_start(argList, format);

    logv(kInfo, format, argList);

    va_end(argList);
}

void CLog::warning(const char* format, ...)
{
    va_list argList;
    va_start(argList, format);

    logv(kWarning, format, argList);

    va_end(argList);
}

void CLog::error(const char* format, ...)
{
    va_list argList;
    va_start(argList, format);

    logv(kError, format, argList);

    va_end(argList);
}

void CLog::fatal(const char* format, ...)
{
    va_list argList;
    va_start(argList, format);

    logv(kFatal, format, argList);

    va_end(argList);
}

CLog::Level CLog::setLevel(Level level)
{
    detail::AutoLock lock(s_context.cs);

    std::swap(s_context.level, level);
    return level;
}

CLog::Level getLevel()
{
    return s_context.level;
}

int CLog::setLogger(LoggerType type, int flags)
{
    int rc = 0;

    detail::AutoLock lock(s_context.cs);

    if (type == COUT)
    {
        s_context.setLogger(type, new detail::LogWriter(detail::writeCout), flags);
    }
    else if (type == SYSLOG)
    {
#ifndef WIN32
        s_context.setLogger(type, new detail::SyslogWriter(), flags);
#endif //WIN32
    }
    else if (type == DEBUGWINDOW)
    {
        s_context.setLogger(type, new detail::LogWriter(detail::writeDebug), flags);
    }
    else if (type == FILE)
    {
        s_context.setLogger(type, new detail::FileLogWriter(s_context.fileName), flags);
    }
    else
    {
        rc = EINVAL;
    }
    return rc;
}

int CLog::setFileParam(const char* filename, int fileSize, int rollCount)
{
    if ((filename == NULL) || (strlen(filename) == 0))
    {
        filename = "app.log";
    }

    if (fileSize <= 0)
    {
        fileSize = detail::kMaxFileSize;
    }

    if (rollCount <= 0)
    {
        rollCount = detail::kMaxFileCount;
    }

    int rc = 0;

    detail::AutoLock lock(s_context.cs);

    strcpy(s_context.fileName, filename);
    s_context.fileSize = fileSize;
    s_context.fileCount = rollCount;

    detail::FileLogWriter* writer = (detail::FileLogWriter*)s_context.logger[FILE];
    if (writer)
    {
    	writer->setFile(filename);
    	writer->setMaxSize(fileSize * 1024 * 1024);
    	writer->setRollCount(rollCount);
    }

    return rc;
}

void CLog::setExtLogger(WriteLog logger, int flags)
{
    detail::AutoLock lock(s_context.cs);

    if (s_context.logger[EXTERNAL])
    {
        delete s_context.logger[EXTERNAL];
        s_context.logger[EXTERNAL] = NULL;
    }

    s_context.logger[EXTERNAL] = new detail::LogWriter(logger);
    s_context.flags[EXTERNAL] = flags;
}

void CLog::resetLogger()
{
    detail::AutoLock lock(s_context.cs);

    s_context.resetLogger();
}

static CLog::Level parseLevel(const std::string& strLevel)
{
    size_t count = sizeof(s_levelEntry)/sizeof(s_levelEntry[0]);
    for (size_t i = 0; i < count; i ++)
    {
        if (strLevel == s_levelEntry[i].name)
        {
            return s_levelEntry[i].level;
        }
    }
    return CLog::kNone;
}

static int parseFormat(const std::string& strFormat)
{
	if (strFormat == "datetime")
	{
		return CLog::DATETIME;
	}
	else if (strFormat == "time")
	{
		return CLog::TIME;
	}
	return 0;
}

static int readFormat(detail::IniFile& iniFile, const char* section, const char* key)
{
	int flags = 0;
	std::string strFormat;
	iniFile.queryValue(section, key, strFormat);
	while (!strFormat.empty())
	{
		size_t pos = strFormat.find(',');
		if (pos != std::string::npos)
		{
			flags |= parseFormat(strFormat.substr(0, pos));
			strFormat = strFormat.substr(pos + 1);
		}
		else
		{
			flags |= parseFormat(strFormat);
            break;
		}
	}
	return flags;
}

int CLog::reload(const char* configPath)
{
    if ((configPath == NULL) || strlen(configPath) == 0)
    {
        return 0;
    }

    detail::IniFile iniFile;
    if (!iniFile.load(configPath))
    {
        return ENOENT;
    }

    resetLogger();

    std::string strLevel;
    iniFile.queryValue("CLog", "Level", strLevel);
    Level level = parseLevel(strLevel);
    setLevel(level);

    bool enabled = false;
    int flags = 0;
    iniFile.queryValue("Console", "Enabled", enabled);
    flags = readFormat(iniFile, "Console", "Format");
    if (enabled)
    {
        setLogger(CLog::COUT, flags);
    }

    iniFile.queryValue("Debug", "Enabled", enabled);
    flags = readFormat(iniFile, "Debug", "Format");
    if (enabled)
    {
        setLogger(CLog::DEBUGWINDOW, flags);
    }

    std::string filename = s_context.fileName;
    int fileSize = s_context.fileSize;
    int fileCount = s_context.fileCount;
    iniFile.queryValue("File", "FileName", filename);
    iniFile.queryValue("File", "MaxSize", fileSize);
    iniFile.queryValue("File", "RollCount", fileCount);

    setFileParam(filename.c_str(), fileSize, fileCount);

    iniFile.queryValue("File", "Enabled", enabled);
    flags = readFormat(iniFile, "File", "Format");
    if (enabled)
    {
        setLogger(CLog::FILE, flags);
    }

    iniFile.queryValue("Syslog", "Enabled", enabled);
    flags = readFormat(iniFile, "Syslog", "Format");
    if (enabled)
    {
        setLogger(CLog::SYSLOG, flags);
    }

    return 0;
}

const char* CLog::getDateTime()
{
	static char time_buffer[128];

	struct tm t;
	int ms = 0;
	detail::getcurtime(t, ms);

	char* buf = time_buffer;
	detail::sprint(&buf, 128, "%04d-%02d-%02d %02d:%02d:%02d.%d",
			(t.tm_year + 1900), (t.tm_mon + 1), t.tm_mday,
			t.tm_hour, t.tm_min, t.tm_sec, ms);

    return time_buffer;
}

const char* CLog::getShortTime()
{
	static char short_time_buffer[128];

	struct tm t;
	int ms = 0;
	detail::getcurtime(t, ms);

	char* buf = short_time_buffer;
	detail::sprint(&buf, 128, "%02d:%02d:%02d.%d",
			t.tm_hour, t.tm_min, t.tm_sec, ms);

    return short_time_buffer;
}

int CLog::getCurTime(int* ms)
{
    struct timeval tv;
    detail::gettimeofday(&tv, NULL);
    if (ms)
    {
        *ms = tv.tv_usec / 1000;
    }
    return tv.tv_sec;
}

const char* CLog::getName(int level)
{
	if (level < 0)
	{
		level = 0;
	}
	else if (level > kFatal)
	{
		level = kFatal;
	}
	return s_levelEntry[level].name;
}

void CLog::setExtLogger(Writer* logger, int flags)
{
    detail::AutoLock lock(s_context.cs);

    if (s_context.logger[EXTERNAL])
    {
        delete s_context.logger[EXTERNAL];
        s_context.logger[EXTERNAL] = NULL;
    }

    s_context.logger[EXTERNAL] = logger;
    s_context.flags[EXTERNAL] = flags;
}


text:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { isPresent, isString } from 'angular2/src/facade/lang';
import { Headers } from './headers';
import { RequestMethod } from './enums';
import { Injectable } from 'angular2/core';
import { URLSearchParams } from './url_search_params';
import { normalizeMethodName } from './http_utils';
/**
 * Creates a request options object to be optionally provided when instantiating a
 * {@link Request}.
 *
 * This class is based on the `RequestInit` description in the [Fetch
 * Spec](https://fetch.spec.whatwg.org/#requestinit).
 *
 * All values are null by default. Typical defaults can be found in the {@link BaseRequestOptions}
 * class, which sub-classes `RequestOptions`.
 *
 * ### Example ([live demo](http://plnkr.co/edit/7Wvi3lfLq41aQPKlxB4O?p=preview))
 *
 * ```typescript
 * import {RequestOptions, Request, RequestMethod} from 'angular2/http';
 *
 * var options = new RequestOptions({
 *   method: RequestMethod.Post,
 *   url: 'https://google.com'
 * });
 * var req = new Request(options);
 * console.log('req.method:', RequestMethod[req.method]); // Post
 * console.log('options.url:', options.url); // https://google.com
 * ```
 */
export class RequestOptions {
    constructor({ method, headers, body, url, search } = {}) {
        this.method = isPresent(method) ? normalizeMethodName(method) : null;
        this.headers = isPresent(headers) ? headers : null;
        this.body = isPresent(body) ? body : null;
        this.url = isPresent(url) ? url : null;
        this.search = isPresent(search) ? (isString(search) ? new URLSearchParams((search)) :
            (search)) :
            null;
    }
    /**
     * Creates a copy of the `RequestOptions` instance, using the optional input as values to override
     * existing values. This method will not change the values of the instance on which it is being
     * called.
     *
     * Note that `headers` and `search` will override existing values completely if present in
     * the `options` object. If these values should be merged, it should be done prior to calling
     * `merge` on the `RequestOptions` instance.
     *
     * ### Example ([live demo](http://plnkr.co/edit/6w8XA8YTkDRcPYpdB9dk?p=preview))
     *
     * ```typescript
     * import {RequestOptions, Request, RequestMethod} from 'angular2/http';
     *
     * var options = new RequestOptions({
     *   method: RequestMethod.Post
     * });
     * var req = new Request(options.merge({
     *   url: 'https://google.com'
     * }));
     * console.log('req.method:', RequestMethod[req.method]); // Post
     * console.log('options.url:', options.url); // null
     * console.log('req.url:', req.url); // https://google.com
     * ```
     */
    merge(options) {
        return new RequestOptions({
            method: isPresent(options) && isPresent(options.method) ? options.method : this.method,
            headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
            body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
            url: isPresent(options) && isPresent(options.url) ? options.url : this.url,
            search: isPresent(options) && isPresent(options.search) ?
                (isString(options.search) ? new URLSearchParams((options.search)) :
                    (options.search).clone()) :
                this.search
        });
    }
}
/**
 * Subclass of {@link RequestOptions}, with default values.
 *
 * Default values:
 *  * method: {@link RequestMethod RequestMethod.Get}
 *  * headers: empty {@link Headers} object
 *
 * This class could be extended and bound to the {@link RequestOptions} class
 * when configuring an {@link Injector}, in order to override the default options
 * used by {@link Http} to create and send {@link Request Requests}.
 *
 * ### Example ([live demo](http://plnkr.co/edit/LEKVSx?p=preview))
 *
 * ```typescript
 * import {provide} from 'angular2/core';
 * import {bootstrap} from 'angular2/platform/browser';
 * import {HTTP_PROVIDERS, Http, BaseRequestOptions, RequestOptions} from 'angular2/http';
 * import {App} from './myapp';
 *
 * class MyOptions extends BaseRequestOptions {
 *   search: string = 'coreTeam=true';
 * }
 *
 * bootstrap(App, [HTTP_PROVIDERS, provide(RequestOptions, {useClass: MyOptions})]);
 * ```
 *
 * The options could also be extended when manually creating a {@link Request}
 * object.
 *
 * ### Example ([live demo](http://plnkr.co/edit/oyBoEvNtDhOSfi9YxaVb?p=preview))
 *
 * ```
 * import {BaseRequestOptions, Request, RequestMethod} from 'angular2/http';
 *
 * var options = new BaseRequestOptions();
 * var req = new Request(options.merge({
 *   method: RequestMethod.Post,
 *   url: 'https://google.com'
 * }));
 * console.log('req.method:', RequestMethod[req.method]); // Post
 * console.log('options.url:', options.url); // null
 * console.log('req.url:', req.url); // https://google.com
 * ```
 */
export let BaseRequestOptions = class BaseRequestOptions extends RequestOptions {
    constructor() {
        super({ method: RequestMethod.Get, headers: new Headers() });
    }
};
BaseRequestOptions = __decorate([
    Injectable(),
    __metadata('design:paramtypes', [])
], BaseRequestOptions);

redpajama_set_name: RedPajamaGithub


text:
/**
 * @file
 * @author Ivan Ristic <ivanr@webkreator.com>
 */

#include "htp_config_auto.h"

#include "htp_private.h"

#define IN_TEST_NEXT_BYTE_OR_RETURN(X) \
if ((X)->in_current_read_offset >= (X)->in_current_len) { \
    return HTP_DATA; \
}

#define IN_PEEK_NEXT(X) \
if ((X)->in_current_read_offset >= (X)->in_current_len) { \
    (X)->in_next_byte = -1; \
} else { \
    (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \
}

#define IN_NEXT_BYTE(X) \
if ((X)->in_current_read_offset < (X)->in_current_len) { \
    (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \
    (X)->in_current_read_offset++; \
    (X)->in_current_consume_offset++; \
    (X)->in_stream_offset++; \
} else { \
    (X)->in_next_byte = -1; \
}

#define IN_NEXT_BYTE_OR_RETURN(X) \
if ((X)->in_current_read_offset < (X)->in_current_len) { \
    (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \
    (X)->in_current_read_offset++; \
    (X)->in_current_consume_offset++; \
    (X)->in_stream_offset++; \
} else { \
    return HTP_DATA; \
}

#define IN_COPY_BYTE_OR_RETURN(X) \
if ((X)->in_current_read_offset < (X)->in_current_len) { \
    (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \
    (X)->in_current_read_offset++; \
    (X)->in_stream_offset++; \
} else { \
    return HTP_DATA_BUFFER; \
}

/**
 * Sends outstanding connection data to the currently active data receiver hook.
 *
 * @param[in] connp
 * @param[in] is_last
 * @return HTP_OK, or a value returned from a callback.
 */
static htp_status_t htp_connp_req_receiver_send_data(htp_connp_t *connp, int is_last) {
    if (connp->in_data_receiver_hook == NULL) return HTP_OK;

    htp_tx_data_t d;
    d.tx = connp->in_tx;
    d.data = connp->in_current_data + connp->in_current_receiver_offset;
    d.len = connp->in_current_read_offset - connp->in_current_receiver_offset;
    d.is_last = is_last;

    htp_status_t rc = htp_hook_run_all(connp->in_data_receiver_hook, &d);
    if (rc != HTP_OK) return rc;

    connp->in_current_receiver_offset = connp->in_current_read_offset;

    return HTP_OK;
}

/**
 * Configures the data receiver hook. If there is a previous hook, it will be finalized and cleared.
 *
 * @param[in] connp
 * @param[in] data_receiver_hook
 * @return HTP_OK, or a value returned from a callback.
 */
static htp_status_t htp_connp_req_receiver_set(htp_connp_t *connp, htp_hook_t *data_receiver_hook) {
    htp_connp_req_receiver_finalize_clear(connp);

    connp->in_data_receiver_hook = data_receiver_hook;
    connp->in_current_receiver_offset = connp->in_current_read_offset;

    return HTP_OK;
}

/**
 * Finalizes an existing data receiver hook by sending any outstanding data to it. The
 * hook is then removed so that it receives no more data.
 *
 * @param[in] connp
 * @return HTP_OK, or a value returned from a callback.
 */
htp_status_t htp_connp_req_receiver_finalize_clear(htp_connp_t *connp) {
    if (connp->in_data_receiver_hook == NULL) return HTP_OK;

    htp_status_t rc = htp_connp_req_receiver_send_data(connp, 1 /* last */);

    connp->in_data_receiver_hook = NULL;

    return rc;
}

/**
 * Handles request parser state changes. At the moment, this function is used only
 * to configure data receivers, which are sent raw connection data.
 *
 * @param[in] connp
 * @return HTP_OK, or a value returned from a callback.
 */
static htp_status_t htp_req_handle_state_change(htp_connp_t *connp) {
    if (connp->in_state_previous == connp->in_state) return HTP_OK;

    if (connp->in_state == htp_connp_REQ_HEADERS) {
        htp_status_t rc = HTP_OK;

        switch (connp->in_tx->request_progress) {
            case HTP_REQUEST_HEADERS:
                rc = htp_connp_req_receiver_set(connp, connp->in_tx->cfg->hook_request_header_data);
                break;

            case HTP_REQUEST_TRAILER:
                rc = htp_connp_req_receiver_set(connp, connp->in_tx->cfg->hook_request_trailer_data);
                break;

            default:
                // Do nothing; receivers are currently used only for header blocks.
                break;
        }

        if (rc != HTP_OK) return rc;
    }

    // Initially, I had the finalization of raw data sending here, but that
    // caused the last REQUEST_HEADER_DATA hook to be invoked after the
    // REQUEST_HEADERS hook -- which I thought made no sense. For that reason,
    // the finalization is now initiated from the request header processing code,
    // which is less elegant but provides a better user experience. Having some
    // (or all) hooks to be invoked on state change might work better.

    connp->in_state_previous = connp->in_state;

    return HTP_OK;
}

/**
 * If there is any data left in the inbound data chunk, this function will preserve
 * it for later consumption. The maximum amount accepted for buffering is controlled
 * by htp_config_t::field_limit_hard.
 *
 * @param[in] connp
 * @return HTP_OK, or HTP_ERROR on fatal failure.
 */
static htp_status_t htp_connp_req_buffer(htp_connp_t *connp) {
    if (connp->in_current_data == NULL) return HTP_OK;

    unsigned char *data = connp->in_current_data + connp->in_current_consume_offset;
    size_t len = connp->in_current_read_offset - connp->in_current_consume_offset;

    if (len == 0)
        return HTP_OK;

    // Check the hard (buffering) limit.

    size_t newlen = connp->in_buf_size + len;

    // When calculating the size of the buffer, take into account the
    // space we're using for the request header buffer.
    if (connp->in_header != NULL) {
        newlen += bstr_len(connp->in_header);
    }

    if (newlen > connp->in_tx->cfg->field_limit_hard) {
        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request buffer over the limit: size %zd limit %zd.",
                newlen, connp->in_tx->cfg->field_limit_hard);
        return HTP_ERROR;
    }

    // Copy the data remaining in the buffer.

    if (connp->in_buf == NULL) {
        connp->in_buf = malloc(len);
        if (connp->in_buf == NULL) return HTP_ERROR;
        memcpy(connp->in_buf, data, len);
        connp->in_buf_size = len;
    } else {
        size_t newsize = connp->in_buf_size + len;
        unsigned char *newbuf = realloc(connp->in_buf, newsize);
        if (newbuf == NULL) return HTP_ERROR;
        connp->in_buf = newbuf;
        memcpy(connp->in_buf + connp->in_buf_size, data, len);
        connp->in_buf_size = newsize;
    }

    // Reset the consumer position.
    connp->in_current_consume_offset = connp->in_current_read_offset;

    return HTP_OK;
}

/**
 * Returns to the caller the memory region that should be processed next. This function
 * hides away the buffering process from the rest of the code, allowing it to work with
 * non-buffered data that's in the inbound chunk, or buffered data that's in our structures.
 *
 * @param[in] connp
 * @param[out] data
 * @param[out] len
 * @return HTP_OK
 */
static htp_status_t htp_connp_req_consolidate_data(htp_connp_t *connp, unsigned char **data, size_t *len) {
    if (connp->in_buf == NULL) {
        // We do not have any data buffered; point to the current data chunk.
        *data = connp->in_current_data + connp->in_current_consume_offset;
        *len = connp->in_current_read_offset - connp->in_current_consume_offset;
    } else {
        // We already have some data in the buffer. Add the data from the current
        // chunk to it, and point to the consolidated buffer.
        if (htp_connp_req_buffer(connp) != HTP_OK) {
            return HTP_ERROR;
        }

        *data = connp->in_buf;
        *len = connp->in_buf_size;
    }

    return HTP_OK;
}

/**
 * Clears buffered inbound data and resets the consumer position to the reader position.
 *
 * @param[in] connp
 */
static void htp_connp_req_clear_buffer(htp_connp_t *connp) {
    connp->in_current_consume_offset = connp->in_current_read_offset;

    if (connp->in_buf != NULL) {
        free(connp->in_buf);
        connp->in_buf = NULL;
        connp->in_buf_size = 0;
    }
}

/**
 * Performs a check for a CONNECT transaction to decide whether inbound
 * parsing needs to be suspended.
 *
 * @param[in] connp
 * @return HTP_OK if the request does not use CONNECT, HTP_DATA_OTHER if
 *          inbound parsing needs to be suspended until we hear from the
 *          other side
 */
htp_status_t htp_connp_REQ_CONNECT_CHECK(htp_connp_t *connp) {
    // If the request uses the CONNECT method, then there will
    // not be a request body, but first we need to wait to see the
    // response in order to determine if the tunneling request
    // was a success.
    if (connp->in_tx->request_method_number == HTP_M_CONNECT) {
        connp->in_state = htp_connp_REQ_CONNECT_WAIT_RESPONSE;
        connp->in_status = HTP_STREAM_DATA_OTHER;
        return HTP_DATA_OTHER;
    }

    // Continue to the next step to determine
    // the presence of request body
    connp->in_state = htp_connp_REQ_BODY_DETERMINE;

    return HTP_OK;
}

/**
 * Determines whether inbound parsing needs to continue or stop. In
 * case the data appears to be plain text HTTP, we try to continue.
 *
 * @param[in] connp
 * @return HTP_OK if the parser can resume parsing, HTP_DATA_BUFFER if
 *         we need more data.
 */
htp_status_t htp_connp_REQ_CONNECT_PROBE_DATA(htp_connp_t *connp) {
    for (;;) {//;i < max_read; i++) {
        IN_PEEK_NEXT(connp);
        // Have we reached the end of the line? For some reason
        // we can't test after IN_COPY_BYTE_OR_RETURN */
        if (connp->in_next_byte == LF || connp->in_next_byte == 0x00)
            break;

        IN_COPY_BYTE_OR_RETURN(connp);

    }

    unsigned char *data;
    size_t len;
    if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) {
        fprintf(stderr, "htp_connp_req_consolidate_data fail");
        return HTP_ERROR;
    }
#ifdef HTP_DEBUG
    fprint_raw_data(stderr, "PROBING", data, len);
#endif

    size_t pos = 0;
    size_t mstart = 0;
    // skip past leading whitespace. IIS allows this
    while ((pos < len) && htp_is_space(data[pos]))
        pos++;
    if (pos)
        mstart = pos;
    // The request method starts at the beginning of the
    // line and ends with the first whitespace character.
    while ((pos < len) && (!htp_is_space(data[pos])))
        pos++;

    int methodi = HTP_M_UNKNOWN;
    bstr *method = bstr_dup_mem(data + mstart, pos - mstart);
    if (method) {
        methodi = htp_convert_method_to_number(method);
        bstr_free(method);
    }
    if (methodi != HTP_M_UNKNOWN) {
#ifdef HTP_DEBUG
        fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel contains plain text HTTP", data, len);
#endif
        connp->in_state = htp_connp_REQ_IDLE;
    } else {
#ifdef HTP_DEBUG
        fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel is not HTTP", data, len);
#endif
        connp->in_status = HTP_STREAM_TUNNEL;
        connp->out_status = HTP_STREAM_TUNNEL;
    }

    // not calling htp_connp_req_clear_buffer, we're not consuming the data

    return HTP_OK;
}

/**
 * Determines whether inbound parsing, which was suspended after
 * encountering a CONNECT transaction, can proceed (after receiving
 * the response).
 *
 * @param[in] connp
 * @return HTP_OK if the parser can resume parsing, HTP_DATA_OTHER if
 *         it needs to continue waiting.
 */
htp_status_t htp_connp_REQ_CONNECT_WAIT_RESPONSE(htp_connp_t *connp) {
    // Check that we saw the response line of the current inbound transaction.
    if (connp->in_tx->response_progress <= HTP_RESPONSE_LINE) {
        return HTP_DATA_OTHER;
    }

    // A 2xx response means a tunnel was established. Anything
    // else means we continue to follow the HTTP stream.
    if ((connp->in_tx->response_status_number >= 200) && (connp->in_tx->response_status_number <= 299)) {
        // TODO Check that the server did not accept a connection to itself.

        // The requested tunnel was established: we are going
        // to probe the remaining data on this stream to see
        // if we need to ignore it or parse it
        connp->in_state = htp_connp_REQ_CONNECT_PROBE_DATA;
    } else {
        // No tunnel; continue to the next transaction
        connp->in_state = htp_connp_REQ_FINALIZE;
    }

    return HTP_OK;
}

/**
 * Consumes bytes until the end of the current line.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA_END(htp_connp_t *connp) {
    // TODO We shouldn't really see anything apart from CR and LF,
    //      so we should warn about anything else.

    for (;;) {
        IN_NEXT_BYTE_OR_RETURN(connp);

        connp->in_tx->request_message_len++;

        if (connp->in_next_byte == LF) {
            connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH;
            return HTP_OK;
        }
    }

    return HTP_ERROR;
}

/**
 * Processes a chunk of data.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA(htp_connp_t *connp) {
    // Determine how many bytes we can consume.
    size_t bytes_to_consume;
    if (connp->in_current_len - connp->in_current_read_offset >= connp->in_chunked_length) {
        // Entire chunk available in the buffer; read all of it.
        bytes_to_consume = connp->in_chunked_length;
    } else {
        // Partial chunk available in the buffer; read as much as we can.
        bytes_to_consume = connp->in_current_len - connp->in_current_read_offset;
    }

    #ifdef HTP_DEBUG
    fprintf(stderr, "htp_connp_REQ_BODY_CHUNKED_DATA Consuming %zd bytes\n", bytes_to_consume);
    #endif

    // If the input buffer is empty, ask for more data.
    if (bytes_to_consume == 0) return HTP_DATA;

    // Consume the data.
    htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, connp->in_current_data + connp->in_current_read_offset, bytes_to_consume);
    if (rc != HTP_OK) return rc;

    // Adjust counters.
    connp->in_current_read_offset += bytes_to_consume;
    connp->in_current_consume_offset += bytes_to_consume;
    connp->in_stream_offset += bytes_to_consume;
    connp->in_tx->request_message_len += bytes_to_consume;
    connp->in_chunked_length -= bytes_to_consume;

    if (connp->in_chunked_length == 0) {
        // End of the chunk.
        connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA_END;
        return HTP_OK;
    }

    // Ask for more data.
    return HTP_DATA;
}

/**
 * Extracts chunk length.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_BODY_CHUNKED_LENGTH(htp_connp_t *connp) {
    for (;;) {
        IN_COPY_BYTE_OR_RETURN(connp);

        // Have we reached the end of the line?
        if (connp->in_next_byte == LF) {
            unsigned char *data;
            size_t len;

            if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) {
                return HTP_ERROR;
            }

            connp->in_tx->request_message_len += len;

            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, "Chunk length line", data, len);
            #endif

            htp_chomp(data, &len);

            connp->in_chunked_length = htp_parse_chunked_length(data, len);

            htp_connp_req_clear_buffer(connp);

            // Handle chunk length.
            if (connp->in_chunked_length > 0) {
                // More data available.
                connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA;
            } else if (connp->in_chunked_length == 0) {
                // End of data.
                connp->in_state = htp_connp_REQ_HEADERS;
                connp->in_tx->request_progress = HTP_REQUEST_TRAILER;
            } else {
                // Invalid chunk length.
                htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request chunk encoding: Invalid chunk length");
                return HTP_ERROR;
            }

            return HTP_OK;
        }
    }

    return HTP_ERROR;
}

/**
 * Processes identity request body.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_BODY_IDENTITY(htp_connp_t *connp) {
    // Determine how many bytes we can consume.
    size_t bytes_to_consume;
    if (connp->in_current_len - connp->in_current_read_offset >= connp->in_body_data_left) {
        bytes_to_consume = connp->in_body_data_left;
    } else {
        bytes_to_consume = connp->in_current_len - connp->in_current_read_offset;
    }

    // If the input buffer is empty, ask for more data.
    if (bytes_to_consume == 0) return HTP_DATA;

    // Consume data.
    int rc = htp_tx_req_process_body_data_ex(connp->in_tx, connp->in_current_data + connp->in_current_read_offset, bytes_to_consume);
    if (rc != HTP_OK) return rc;

    // Adjust counters.
    connp->in_current_read_offset += bytes_to_consume;
    connp->in_current_consume_offset += bytes_to_consume;
    connp->in_stream_offset += bytes_to_consume;
    connp->in_tx->request_message_len += bytes_to_consume;
    connp->in_body_data_left -= bytes_to_consume;

    if (connp->in_body_data_left == 0) {
        // End of request body.
        connp->in_state = htp_connp_REQ_FINALIZE;
        return HTP_OK;
    }

    // Ask for more data.
    return HTP_DATA;
}

/**
 * Determines presence (and encoding) of a request body.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_BODY_DETERMINE(htp_connp_t *connp) {
    // Determine the next state based on the presence of the request
    // body, and the coding used.
    switch (connp->in_tx->request_transfer_coding) {

        case HTP_CODING_CHUNKED:
            connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH;
            connp->in_tx->request_progress = HTP_REQUEST_BODY;
            break;

        case HTP_CODING_IDENTITY:
            connp->in_content_length = connp->in_tx->request_content_length;
            connp->in_body_data_left = connp->in_content_length;

            if (connp->in_content_length != 0) {
                connp->in_state = htp_connp_REQ_BODY_IDENTITY;
                connp->in_tx->request_progress = HTP_REQUEST_BODY;
            } else {
                connp->in_tx->connp->in_state = htp_connp_REQ_FINALIZE;
            }
            break;

        case HTP_CODING_NO_BODY:
            // This request does not have a body, which
            // means that we're done with it
            connp->in_state = htp_connp_REQ_FINALIZE;
            break;

        default:
            // Should not be here
            return HTP_ERROR;
            break;
    }

    return HTP_OK;
}

/**
 * Parses request headers.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_HEADERS(htp_connp_t *connp) {
    for (;;) {
        IN_COPY_BYTE_OR_RETURN(connp);

        // Have we reached the end of the line?
        if (connp->in_next_byte == LF) {
            unsigned char *data;
            size_t len;

            if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) {
                return HTP_ERROR;
            }

            #ifdef HTP_DEBUG
            fprint_raw_data(stderr, __func__, data, len);
            #endif

            // Should we terminate headers?
            if (htp_connp_is_line_terminator(connp, data, len)) {
                // Parse previous header, if any.
                if (connp->in_header != NULL) {
                    if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header),
                            bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR;

                    bstr_free(connp->in_header);
                    connp->in_header = NULL;
                }

                htp_connp_req_clear_buffer(connp);

                // We've seen all the request headers.
                return htp_tx_state_request_headers(connp->in_tx);
            }

            htp_chomp(data, &len);

            // Check for header folding.
            if (htp_connp_is_line_folded(data, len) == 0) {
                // New header line.

                // Parse previous header, if any.
                if (connp->in_header != NULL) {
                    if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header),
                            bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR;

                    bstr_free(connp->in_header);
                    connp->in_header = NULL;
                }

                IN_PEEK_NEXT(connp);

                if (htp_is_folding_char(connp->in_next_byte) == 0) {
                    // Because we know this header is not folded, we can process the buffer straight away.
                    if (connp->cfg->process_request_header(connp, data, len) != HTP_OK) return HTP_ERROR;
                } else {
                    // Keep the partial header data for parsing later.
                    connp->in_header = bstr_dup_mem(data, len);
                    if (connp->in_header == NULL) return HTP_ERROR;
                }
            } else {
                // Folding; check that there's a previous header line to add to.
                if (connp->in_header == NULL) {
                    // Invalid folding.

                    // Warn only once per transaction.
                    if (!(connp->in_tx->flags & HTP_INVALID_FOLDING)) {
                        connp->in_tx->flags |= HTP_INVALID_FOLDING;
                        htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid request field folding");
                    }

                    // Keep the header data for parsing later.
                    connp->in_header = bstr_dup_mem(data, len);
                    if (connp->in_header == NULL) return HTP_ERROR;
                } else {
                    // Add to the existing header.
                    bstr *new_in_header = bstr_add_mem(connp->in_header, data, len);
                    if (new_in_header == NULL) return HTP_ERROR;
                    connp->in_header = new_in_header;
                }
            }

            htp_connp_req_clear_buffer(connp);
        }
    }

    return HTP_ERROR;
}

/**
 * Determines request protocol.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_PROTOCOL(htp_connp_t *connp) {
    // Is this a short-style HTTP/0.9 request? If it is,
    // we will not want to parse request headers.
    if (connp->in_tx->is_protocol_0_9 == 0) {
        // Switch to request header parsing.
        connp->in_state = htp_connp_REQ_HEADERS;
        connp->in_tx->request_progress = HTP_REQUEST_HEADERS;
    } else {
        // We're done with this request.
        connp->in_state = htp_connp_REQ_FINALIZE;
    }

    return HTP_OK;
}

/**
 * Parse the request line.
 *
 * @param[in] connp
 * @returns HTP_OK on succesful parse, HTP_ERROR on error.
 */
htp_status_t htp_connp_REQ_LINE_complete(htp_connp_t *connp) {
    unsigned char *data;
    size_t len;

    if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) {
        return HTP_ERROR;
    }

    #ifdef HTP_DEBUG
    fprint_raw_data(stderr, __func__, data, len);
    #endif

    // Is this a line that should be ignored?
    if (htp_connp_is_line_ignorable(connp, data, len)) {
        // We have an empty/whitespace line, which we'll note, ignore and move on.
        connp->in_tx->request_ignored_lines++;

        htp_connp_req_clear_buffer(connp);

        return HTP_OK;
    }

    // Process request line.

    htp_chomp(data, &len);

    connp->in_tx->request_line = bstr_dup_mem(data, len);
    if (connp->in_tx->request_line == NULL)
        return HTP_ERROR;

    if (connp->cfg->parse_request_line(connp) != HTP_OK)
        return HTP_ERROR;

    // Finalize request line parsing.

    if (htp_tx_state_request_line(connp->in_tx) != HTP_OK)
        return HTP_ERROR;

    htp_connp_req_clear_buffer(connp);

    return HTP_OK;
}

/**
 * Parses request line.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_LINE(htp_connp_t *connp) {
    for (;;) {
        // Get one byte
        IN_COPY_BYTE_OR_RETURN(connp);

        // Have we reached the end of the line?
        if (connp->in_next_byte == LF) {
            return htp_connp_REQ_LINE_complete(connp);
        }
    }

    return HTP_ERROR;
}

htp_status_t htp_connp_REQ_FINALIZE(htp_connp_t *connp) {
    return htp_tx_state_request_complete(connp->in_tx);
}

htp_status_t htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9(htp_connp_t *connp) {
    // Consume whatever is left in the buffer.

    size_t bytes_left = connp->in_current_len - connp->in_current_read_offset;

    if (bytes_left > 0) {
        connp->conn->flags |= HTP_CONN_HTTP_0_9_EXTRA;
    }

    connp->in_current_read_offset += bytes_left;
    connp->in_current_consume_offset += bytes_left;
    connp->in_stream_offset += bytes_left;

    return HTP_DATA;
}

/**
 * The idle state is where the parser will end up after a transaction is processed.
 * If there is more data available, a new request will be started.
 *
 * @param[in] connp
 * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed.
 */
htp_status_t htp_connp_REQ_IDLE(htp_connp_t * connp) {
    // We want to start parsing the next request (and change
    // the state from IDLE) only if there's at least one
    // byte of data available. Otherwise we could be creating
    // new structures even if there's no more data on the
    // connection.
    IN_TEST_NEXT_BYTE_OR_RETURN(connp);

    connp->in_tx = htp_connp_tx_create(connp);
    if (connp->in_tx == NULL) return HTP_ERROR;

    // Change state to TRANSACTION_START
    htp_tx_state_request_start(connp->in_tx);

    return HTP_OK;
}

/**
 * Returns how many bytes from the current data chunks were consumed so far.
 *
 * @param[in] connp
 * @return The number of bytes consumed.
 */
size_t htp_connp_req_data_consumed(htp_connp_t *connp) {
    return connp->in_current_read_offset;
}

int htp_connp_req_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len) {
    #ifdef HTP_DEBUG
    fprintf(stderr, "htp_connp_req_data(connp->in_status %x)\n", connp->in_status);
    fprint_raw_data(stderr, __func__, data, len);
    #endif

    // Return if the connection is in stop state.
    if (connp->in_status == HTP_STREAM_STOP) {
        htp_log(connp, HTP_LOG_MARK, HTP_LOG_INFO, 0, "Inbound parser is in HTP_STREAM_STOP");
        return HTP_STREAM_STOP;
    }

    // Return if the connection had a fatal error earlier
    if (connp->in_status == HTP_STREAM_ERROR) {
        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Inbound parser is in HTP_STREAM_ERROR");

        #ifdef HTP_DEBUG
        fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (previous error)\n");
        #endif

        return HTP_STREAM_ERROR;
    }

    // Sanity check: we must have a transaction pointer if the state is not IDLE (no inbound transaction)
    if ((connp->in_tx == NULL)&&(connp->in_state != htp_connp_REQ_IDLE)) {
        connp->in_status = HTP_STREAM_ERROR;

        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Missing inbound transaction data");

        return HTP_STREAM_ERROR;
    }

    // If the length of the supplied data chunk is zero, proceed
    // only if the stream has been closed. We do not allow zero-sized
    // chunks in the API, but we use them internally to force the parsers
    // to finalize parsing.
    if (((data == NULL) || (len == 0)) && (connp->in_status != HTP_STREAM_CLOSED)) {
        htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Zero-length data chunks are not allowed");

        #ifdef HTP_DEBUG
        fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (zero-length chunk)\n");
        #endif

        return HTP_STREAM_CLOSED;
    }

    // Remember the timestamp of the current request data chunk
    if (timestamp != NULL) {
        memcpy(&connp->in_timestamp, timestamp, sizeof (*timestamp));
    }

    // Store the current chunk information
    connp->in_current_data = (unsigned char *) data;
    connp->in_current_len = len;
    connp->in_current_read_offset = 0;
    connp->in_current_consume_offset = 0;
    connp->in_current_receiver_offset = 0;
    connp->in_chunk_count++;

    htp_conn_track_inbound_data(connp->conn, len, timestamp);

    // Return without processing any data if the stream is in tunneling
    // mode (which it would be after an initial CONNECT transaction).
    if (connp->in_status == HTP_STREAM_TUNNEL) {
        #ifdef HTP_DEBUG
        fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n");
        #endif

        return HTP_STREAM_TUNNEL;
    }

    if (connp->out_status == HTP_STREAM_DATA_OTHER) {
        connp->out_status = HTP_STREAM_DATA;
    }

    // Invoke a processor, in a loop, until an error
    // occurs or until we run out of data. Many processors
    // will process a request, each pointing to the next
    // processor that needs to run.
    for (;;) {
        #ifdef HTP_DEBUG
        fprintf(stderr, "htp_connp_req_data: in state=%s, progress=%s\n",
                htp_connp_in_state_as_string(connp),
                htp_tx_request_progress_as_string(connp->in_tx));
        #endif

        // Return if there's been an error or if we've run out of data. We are relying
        // on processors to supply error messages, so we'll keep quiet here.
        htp_status_t rc = connp->in_state(connp);
        if (rc == HTP_OK) {
            if (connp->in_status == HTP_STREAM_TUNNEL) {
                #ifdef HTP_DEBUG
                fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n");
                #endif

                return HTP_STREAM_TUNNEL;
            }

            rc = htp_req_handle_state_change(connp);
        }

        if (rc != HTP_OK) {
            // Do we need more data?
            if ((rc == HTP_DATA) || (rc == HTP_DATA_BUFFER)) {
                htp_connp_req_receiver_send_data(connp, 0 /* not last */);

                if (rc == HTP_DATA_BUFFER) {
                    if (htp_connp_req_buffer(connp) != HTP_OK) {
                        connp->in_status = HTP_STREAM_ERROR;
                        return HTP_STREAM_ERROR;
                    }
                }

                #ifdef HTP_DEBUG
                fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA\n");
                #endif

                connp->in_status = HTP_STREAM_DATA;

                return HTP_STREAM_DATA;
            }

            // Check for suspended parsing.
            if (rc == HTP_DATA_OTHER) {
                // We might have actually consumed the entire data chunk?
                if (connp->in_current_read_offset >= connp->in_current_len) {
                    // Do not send STREAM_DATE_DATA_OTHER if we've consumed the entire chunk.

                    #ifdef HTP_DEBUG
                    fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (suspended parsing)\n");
                    #endif

                    connp->in_status = HTP_STREAM_DATA;

                    return HTP_STREAM_DATA;
                } else {
                    // Partial chunk consumption.

                    #ifdef HTP_DEBUG
                    fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA_OTHER\n");
                    #endif

                    connp->in_status = HTP_STREAM_DATA_OTHER;

                    return HTP_STREAM_DATA_OTHER;
                }
            }

            // Check for the stop signal.
            if (rc == HTP_STOP) {
                #ifdef HTP_DEBUG
                fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_STOP\n");
                #endif

                connp->in_status = HTP_STREAM_STOP;

                return HTP_STREAM_STOP;
            }

            #ifdef HTP_DEBUG
            fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_ERROR\n");
            #endif

            // Permanent stream error.
            connp->in_status = HTP_STREAM_ERROR;

            return HTP_STREAM_ERROR;
        }
    }

    // Permanent stream error.
    connp->in_status = HTP_STREAM_ERROR;

    return HTP_STREAM_ERROR;
}


text:
package coding

import (
	"fmt"
)

type Project struct {
	CreatedAt             int64  `json:"created_at"`               // "created_at": 1426246044000
	BackendProjectPath    string `json:"backend_project_path"`     // "backend_project_path": "/user/baoti/project/Coding-API",
	Description           string `json:"description"`              // "description": "Coding 的 API 是啥样呢?瞧一瞧,看一看。\n注意:由于官方未提供 API 文档,此处 API 整理自 Coding-Android 项目源码。\n尚未对照 IOS 客户端源码。请前往演示, 或转至 http://coding-api.coding.io/ 以查看当前整理的 API.",
	GitURL                string `json:"git_url"`                  // "git_url": "git://coding.net/baoti/Coding-API.git",
	SshURL                string `json:"ssh_url"`                  // "ssh_url": "git@coding.net:baoti/Coding-API.git",
	IsPublic              bool   `json:"is_public"`                // "is_public": true,
	HttpsURL              string `json:"https_url"`                // "https_url": "https://coding.net/baoti/Coding-API.git",
	ID                    int64  `json:"id"`                       // "id": 67965,
	Name                  string `json:"name"`                     // "name": "Coding-API",
	OwnerID               int64  `json:"owner_id"`                 // "owner_id": 84337,
	OwnerUserName         string `json:"owner_user_name"`          // "owner_user_name": "baoti",
	OwnerUserPicture      string `json:"owner_user_picture"`       // "owner_user_picture": "/static/fruit_avatar/Fruit-2.png",
	OwnerUserHome         string `json:"owner_user_home"`          // "owner_user_home": "baoti",
	ProjectPath           string `json:"project_path"`             // "project_path": "/u/baoti/p/Coding-API",
	Status                int    `json:"status"`                   // "status": 1,
	Type                  int    `json:"type"`                     // "type": 1,
	UpdatedAt             int64  `json:"updated_at"`               // "updated_at": 1426576642885,
	LastUpdated           int64  `json:"last_updated"`             // "last_updated": 1426576630625,
	ForkCount             int    `json:"fork_count"`               // "fork_count": 0,
	StarCount             int    `json:"star_count"`               // "star_count": 2,
	WatchCount            int    `json:"watch_count"`              // "watch_count": 5,
	Pin                   bool   `json:"pin"`                      // "pin": false,
	DepotPath             string `json:"depot_path"`               // "depot_path": "/u/baoti/p/Coding-API/git",
	Forked                bool   `json:"forked"`                   // "forked": false,
	UnReadActivitiesCount int    `json:"un_read_activities_count"` // "un_read_activities_count": 0,
	Icon                  string `json:"icon"`                     // "icon": "/static/project_icon/scenery-23.png",
	CurrentUserRoleID     int    `json:"current_user_role_id"`     // "current_user_role_id": 100,
	CurrentUserRole       string `json:"current_user_role"`        // "current_user_role": "owner",
	Stared                bool   `json:"stared"`                   // "stared": false,
	Watched               bool   `json:"watched"`                  // "watched": false,
	Recommended           int    `json:"recommended"`              // "recommended": 1,
	MaxMember             int    `json:"max_member"`               // "max_member": 10,
	GroupID               int    `json:"groupId"`                  // "groupId": 0
}

type ProjectService struct {
	client *Client
}

// API:  GET /api/user/{user_name}/project/{project_name}
func (s *ProjectService) GetProject(owner, project string) (*Project, error) {
	req, err := s.client.NewRequest("GET",
		fmt.Sprintf("/user/%s/project/%s", owner, project),
		nil)
	if err != nil {
		return nil, err
	}

	out := struct {
		Code    int      `json:"code"`
		Project *Project `json:"data"`
	}{}

	_, err = s.client.Do(req, &out)
	return out.Project, err
}

// 用户的项目列表 ( scope=project )
// API: GET /api/user/projects
func (s *ProjectService) GetUserProjects() ([]*Project, error) {
	req, err := s.client.NewRequest("GET", "/user/projects", nil)
	if err != nil {
		return nil, err
	}
	out := struct {
		Code int `json:"code"`
		Data struct {
			Page      int        `json:"page"`
			PageSize  int        `json:"pageSize"`
			TotalPage int        `json:"totalPage"`
			TotalRow  int        `json:"totalRow"`
			Projects  []*Project `json:"list"`
		} `json:"data"`
	}{}

	_, err = s.client.Do(req, &out)
	return out.Data.Projects, err
}

type File struct {
	Data string `json:"data"`
	Path string `json:"path"`
	Name string `json:"name"`
}

// 读取 repo 某个文件 ( scope=project, project:depot )
// API: GET /api/user/{user_name}/project/{project_name}/git/bolb/{branch_name}/{file_path}
func (s *ProjectService) GetProjectRaw(owner, project, path, branch string) ([]byte, error) {
	req, err := s.client.NewRequest("GET",
		fmt.Sprintf("/user/%s/project/%s/git/blob/%s/%s", owner, project, branch, path),
		nil)
	if err != nil {
		return nil, err
	}

	out := struct {
		Code int `json:"code"`
		Data struct {
			File File `json:"file"`
		} `json:"data"`
	}{}

	_, err = s.client.Do(req, &out)
	return []byte(out.Data.File.Data), err
}


text:
:: *************************************************************************
:: Copyright 2009 VMware, Inc.  All rights reserved.
:: *************************************************************************

::
:: This script is used by New-VmGuestRoute cmdlet to create routes on the guest OS
::
:: PARAMETERS
::	 destination - destination address
::   netmask - route netmask
::   gateway - gateway ip
::   interface - interface id
::
:: USAGE
::   The script is invoked with the following line:
::      NewVMGuestRoute_WindowsGuest.bat <destination> <netmask> <gateway> <interface>
::
:: OUTPUT
::   If this script prints output it is displayed as an error message to the user
::
::

@echo off

set destination=%1

if not [%2]==[""] (
  set mask=MASK %2
)

if not [%3]==[""] (
  set gateway=%3
)

if not [%4]==[""] (
  set interface=IF %4
)

route -p ADD %destination% %mask%  %gateway% %interface%


text:
package de.spektrumprojekt.informationextraction.extractors;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class KeyphraseCandidates implements Iterable<KeyphraseCandidate> {

    final class CandidateLengthComparator implements Comparator<KeyphraseCandidate> {
        @Override
        public int compare(KeyphraseCandidate c1, KeyphraseCandidate c2) {
            return Integer.valueOf(c1.getStemmedValue().length()).compareTo(
                    c2.getStemmedValue().length());
        }
    }

    private final Map<String, KeyphraseCandidate> candidates;

    public KeyphraseCandidates() {
        this.candidates = new HashMap<String, KeyphraseCandidate>();
    }

    public void addCandidate(String stemmedValue, String unstemmedValue, int count) {
        KeyphraseCandidate candidate = candidates.get(stemmedValue);
        if (candidate == null) {
            candidate = new KeyphraseCandidate(stemmedValue, unstemmedValue, count);
            candidates.put(stemmedValue, candidate);
        } else {
            candidate.add(unstemmedValue, count);
        }
    }

    public KeyphraseCandidate getCandidate(String stemmedValue) {
        return candidates.get(stemmedValue);
    }

    @Override
    public Iterator<KeyphraseCandidate> iterator() {
        return candidates.values().iterator();
    }

    /**
     * <p>
     * Remove overlapping phrases, e.g. we extracted the 2-gram "San Francisco" three times and the
     * 1-gram "Francisco" four times, the count of "Francisco" is decremented to one, as it is
     * contained in the longer gram "San Francisco" which occurs three times.
     * </p>
     *
     * @param candidates
     * @return
     */
    public void removeOverlaps() {
        List<KeyphraseCandidate> temp = new ArrayList<KeyphraseCandidate>(candidates.values());
        Collections.sort(temp, new CandidateLengthComparator());
        for (int i = 0; i < temp.size(); i++) {
            KeyphraseCandidate current = temp.get(i);
            int highestOverlap = 0;
            for (int j = i + 1; j < temp.size(); j++) {
                KeyphraseCandidate other = temp.get(j);
                if (other.getStemmedValue().contains(current.getStemmedValue())) {
                    highestOverlap = Math.max(highestOverlap, other.getCount());
                }
            }
            if (highestOverlap > 0) {
                current.decrementCount(highestOverlap);
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        for (KeyphraseCandidate cand : this) {
            stringBuilder.append(cand).append(",");
        }
        return stringBuilder.toString();
    }

}


text:
class Building < ActiveRecord::Base
  belongs_to :location
end


text:
layout: post
title: "Please question the process"
author: "Marcus Hammarberg"
date: 2015-04-06 08:51:39
tags:
 - Agile
 - Lean
 - Leadership
---

One of the things that I've been told that upset me the most during the last couple of years was this sentence:

Please don't question the process

Instead of going angry and rant about the stupidity of that I'd thought about the opposite. Or what I'd want to do. If I ever get to hire people to a company I will tell them, on their first day:

Please question the process. This is only as good as we've got so far. In fact - one of the big reasons you are here is to make us better. You cannot do that without questioning and challenging the current state.

If you ever feel that something could be done better or in another way - please say so.

Please question the process means that we are humble about that we are not the best we will ever be. It also sends a signal about us looking for something better. Finally by saying "Please question the process" the person you say it to feels like they are important, that you need their input. And it includes new people in "we", in the team instantly.


text:
package com.qinyadan.monitor.plugin;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import com.qinyadan.monitor.logger.LoggerFactory;
import com.qinyadan.monitor.logger.Logger;

public class PluginResourcesResolver {
	private static Logger logger = LoggerFactory.getLogger(PluginResourcesResolver.class);

	public List<URL> getResources(){
		List<URL> cfgUrlPaths = new ArrayList<URL>();
		Enumeration<URL> urls;
		try {
			urls = getDefaultClassLoader().getResources("monitor-plugin.def");

			if(!urls.hasMoreElements()){
				logger.info("no plugin files (monitor-plugin.properties) found");
			}

			while(urls.hasMoreElements()){
				URL pluginUrl = urls.nextElement();
				cfgUrlPaths.add(pluginUrl);
				logger.info("find monitor plugin define in {}", pluginUrl);
			}

			return cfgUrlPaths;
		} catch (IOException e) {
			logger.error("read resources failure.", e);
		}
		return null;
	}

	private ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back to system class loader...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = PluginResourcesResolver.class.getClassLoader();
		}
		return cl;
	}

}


text:
package com.nexr.hive.excel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hive.serde.Constants;
import org.apache.hadoop.hive.serde2.AbstractSerDe;
import org.apache.hadoop.hive.serde2.SerDeException;
import org.apache.hadoop.hive.serde2.SerDeStats;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector.PrimitiveCategory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.typeinfo.StructTypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
import org.apache.hadoop.io.Writable;
import org.apache.poi.ss.usermodel.Cell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExcelSerDe extends AbstractSerDe {
	private static final Logger logger = LoggerFactory.getLogger(ExcelSerDe.class);

	private final ExcelRowWritable row = new ExcelRowWritable();

	private List<String> columnNames;
	private List<TypeInfo> columnTypes;

	private StructTypeInfo structTypeInfo;
	private ObjectInspector objectInspector;

	@Override
	public List<Cell> deserialize(Writable writable) throws SerDeException {
		logger.debug("___________________________________deserialize");
		return ((ExcelRowWritable) writable).get();
	}

	@Override
	public ObjectInspector getObjectInspector() throws SerDeException {
		return objectInspector;
	}

	@Override
	public SerDeStats getSerDeStats() {
		return null;
	}

	@Override
	public void initialize(Configuration configuration, Properties properties)
			throws SerDeException {
		logger.debug("___________________________________initialize");

		String columnName = properties.getProperty(Constants.LIST_COLUMNS);
		String columnType = properties.getProperty(Constants.LIST_COLUMN_TYPES);
		logger.debug(String.format("columnName : %s, columnType : %s", columnName, columnType));

		if (StringUtils.isNotEmpty(columnName)) {
			columnNames = Arrays.asList(columnName.split(","));
		} else {
			columnNames = new ArrayList<String>();
		}

		if (StringUtils.isNotEmpty(columnType)) {
			columnTypes = TypeInfoUtils.getTypeInfosFromTypeString(columnType);
		} else {
			columnTypes = new ArrayList<TypeInfo>();
		}

		PrimitiveCategory[] columnTypes = toTypes(columnType.split(":"));
		PrimitiveObjectInspector[] columnOIsArray = toPrimitiveJavaOIs(columnTypes);
		objectInspector = ObjectInspectorFactory.getStandardStructObjectInspector(columnNames,
				new ArrayList<ObjectInspector>(Arrays.asList(columnOIsArray)));
	}

	@Override
	public Class<? extends Writable> getSerializedClass() {
		return ExcelRowWritable.class;
	}

	@Override
	public Writable serialize(Object row, ObjectInspector inspector)
			throws SerDeException {
		return this.row;
	}

	private static PrimitiveCategory[] toTypes(String[] types) {
		PrimitiveCategory[] categories = new PrimitiveCategory[types.length];
		for (int i = 0; i < types.length; i++) {
			PrimitiveObjectInspectorUtils.PrimitiveTypeEntry entry =
					PrimitiveObjectInspectorUtils.getTypeEntryFromTypeName(types[i]);
			if (entry == null) {
				throw new IllegalArgumentException("Invalid primitive type " + types[i]);
			}
			categories[i] = entry.primitiveCategory;
		}
		return categories;
	}

	private static PrimitiveObjectInspector[] toPrimitiveJavaOIs(PrimitiveCategory[] categories) {
		PrimitiveObjectInspector[] inspectors = new PrimitiveObjectInspector[categories.length];
		for (int i = 0; i < categories.length; i++) {
			inspectors[i] = PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector(categories[i]);
		}
		return inspectors;
	}

}


text:
package bomFarming;

import org.parabot.core.Context;
import org.parabot.environment.api.utils.Time;
import org.parabot.environment.scripts.Script;
import org.parabot.environment.scripts.framework.Strategy;
import org.rev317.min.api.methods.Game;
import org.rev317.min.api.methods.Inventory;
import org.rev317.min.api.methods.SceneObjects;
import org.rev317.min.api.methods.Items.Option;
import org.rev317.min.api.methods.Menu;
import org.rev317.min.api.wrappers.SceneObject;

public class ClearPatch implements Strategy{

	@Override
	public boolean activate() {
		BomFarming.hasRAKE = Inventory.contains(BomFarming.RAKE) && Game.isLoggedIn();
		if (!BomFarming.hasRAKE){Context.getInstance().getRunningScript().setState(Script.STATE_STOPPED);return false;}

		return BomFarming.hasRAKE && BomFarming.planted;
	}

	@Override
	public void execute() {
		SceneObject patch = null;
		try {
			patch = SceneObjects.getNearest(8151)[0];
			}
		catch(Exception e)
			{
				System.out.println(e);
				Context.getInstance().getRunningScript().setState(Script.STATE_STOPPED);
				return;
			}

		Inventory.getItem(BomFarming.RAKE).interact(Option.USE);
		Time.sleep(200);
		Menu.sendAction(62,patch.getHash(),patch.getLocalRegionX(),patch.getLocalRegionY(),patch.getId(),1);

		Time.sleep(1800);
		BomFarming.planted=!BomFarming.planted;
	}

}


text:
package ru.stqa.pft.addressbook.generators;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.thoughtworks.xstream.XStream;
import ru.stqa.pft.addressbook.model.ContactData;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by SK on 20.05.2016.
 */
public class ContactDataGenerator {
    @Parameter (names ="-c", description = "Contacts count")
    public int count;

    @Parameter(names ="-f", description = "Target file")
    public String file;

    @Parameter(names ="-d", description = "Data format")
    public String format;

    public static void main (String [] args) throws IOException {
        ContactDataGenerator generator = new ContactDataGenerator();
        JCommander jCommander = new JCommander(generator);
        try {
            jCommander.parse(args);
        } catch (ParameterException ex) {
            jCommander.usage();
            return;
        }
        generator.run();
    }

    private void run() throws IOException {
        List<ContactData> contacts = generateContacts(count);
        if (format.equals("xml")) {
            saveAsXml(contacts, new File(file));
        }  else {
            System.out.println("Unrecognized format" + format);
        }
    }

    private void saveAsXml(List<ContactData> contacts, File file) throws IOException {
        XStream xstream = new XStream ();
        xstream.processAnnotations(ContactData.class);
        String xml = xstream.toXML(contacts);
        try (Writer writer = new FileWriter(file)) {
            writer.write(xml);
        }
    }

    private List<ContactData> generateContacts(int count) {
        List <ContactData> contacts = new ArrayList<ContactData>();
        for (int i=0; i<count;i++) {
            File photo = new File("src/test/resources/stru.png");
            contacts.add(new ContactData().withName(String.format("test1 %s", i)).withLastname(String.format("test2 %s", i))
//                    .withGroup(String.format("test1"))
                    .withMobilePhone(String.format("111 %s", i)).withHomePhone(String.format("222 %s", i)).withWorkPhone(String.format("333 %s", i))
                    .withEmail1(String.format("test1 %s", i)).withEmail2(String.format("test2 %s", i)).withEmail3(String.format("test3 %s", i))
            .withPhoto(photo));
        }
        return contacts;
    }
}