viewvc-4intranet/misc/tparse/tparse.h

307 lines
7.0 KiB
C++

/*
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# This file has been rewritten in C++ from the rcsparse.py file by
# Lucas Bruand <lucas.bruand@ecl2002.ec-lyon.fr>
#
# This file was originally based on portions of the blame.py script by
# Curt Hagenlocher.
#
# -----------------------------------------------------------------------
*/
/*
This C++ library offers an API to a performance-oriented RCSFILE parser.
It does little syntax checking.
Version: $Id$
*/
#ifndef __PARSE_H
#define __PARSE_H
#include <memory> /* for auto_ptr */
#include <algorithm> /* for iterator */
#include <exception> /* for exception */
#include <istream> /* for istream */
#include <list> /* for list<> */
#include <string> /* for string */
#define CHUNK_SIZE 30000
#define DEFAULT_TOKEN_SIZE 512
#define DEFAULT_TOKEN_DELTA 10240
#ifndef FALSE
#define FALSE (0 != 0)
#endif
#ifndef TRUE
#define TRUE (0 == 0)
#endif
using namespace std;
/* This class represents a exception that occured during the parsing
of a file */
class RCSParseError : public exception
{
public:
string value;
RCSParseError() {};
RCSParseError(const char *myvalue)
{
value = myvalue;
};
virtual ~RCSParseError() throw() {};
};
class RCSIllegalCharacter : public RCSParseError
{
public:
RCSIllegalCharacter(const char *myvalue)
{
value = myvalue;
};
virtual ~RCSIllegalCharacter() throw() {};
};
class RCSExpected : public RCSParseError
{
public:
string got;
string wanted;
RCSExpected(const char *mygot, const char *mywanted)
{
got = mygot;
wanted = mywanted;
};
RCSExpected(const char *mygot, const char c)
{
got = mygot;
wanted = c;
};
virtual ~RCSExpected() throw() {};
};
class rcstoken
{
public:
size_t length, size, delta;
char *data;
public:
rcstoken(const char *mydata, size_t mylen)
{
init(mydata, mylen);
};
rcstoken(const char *mydata)
{
init(mydata, strlen(mydata));
};
rcstoken(size_t mysize = DEFAULT_TOKEN_SIZE,
size_t mydelta = DEFAULT_TOKEN_DELTA)
{
data = NULL;
size = mysize;
length = 0;
delta = mydelta;
};
~rcstoken()
{
if (data)
free(data);
data = NULL;
};
void init(const char *mydata, size_t mylen);
int null_token()
{
return data == NULL;
};
rcstoken& operator=(const char b)
{
grow(2);
length = 1;
data[0] = b;
data[1] = 0;
return *this;
};
rcstoken& operator+=(const char b)
{
append(b);
return *this;
};
rcstoken& operator+=(rcstoken& token)
{
append(token);
return *this;
};
int operator==(const char *b)
{
size_t b_len;
return data && b && length == (b_len = strlen(b)) &&
memcmp(data, b, (b_len<length) ? b_len : length) == 0;
};
int operator!=(const char *b)
{
return (! (*this == b));
};
int operator==(const char b)
{
return (length == 1) && data && (*data == b);
};
int operator!=(const char b)
{
return (! (*this==b));
};
char operator[](size_t i)
{
return data[i];
};
void append(const char *b, size_t b_len);
void append(const char b)
{
grow(length+2);
data[length] = b;
data[length++] = 0;
};
void append(rcstoken& token)
{
append(token.data, token.length);
};
void grow(size_t new_size);
rcstoken *copy_begin_end(size_t begin, size_t end);
rcstoken *copy_begin_len(size_t begin, size_t len);
};
typedef list<rcstoken> tokenlist;
typedef tokenlist::iterator tokenlist_iter;
/* This class is a handler that receive the event generated by the parser
i.e.: When we reach the head revision tag, etc... */
class Sink
{
public:
Sink() {};
virtual ~Sink() throw () {};
virtual void set_head_revision(rcstoken &revision) = 0;
virtual void set_principal_branch(rcstoken &branch_name) = 0;
virtual void define_tag(rcstoken &name, rcstoken &revision) = 0;
virtual void set_comment(rcstoken &comment) = 0;
virtual void set_description(rcstoken &description) = 0;
virtual void define_revision(rcstoken &revision, long timestamp,
rcstoken &author, rcstoken &state,
tokenlist &branches, rcstoken &next) = 0;
virtual void set_revision_info(rcstoken &revision,
rcstoken &log, rcstoken &text) = 0;
virtual void tree_completed() = 0;
virtual void parse_completed() = 0;
};
/* The class is used to get one by one every token in the file. */
class TokenParser
{
private:
istream *input;
char buf[CHUNK_SIZE];
int buflength;
int idx;
rcstoken *backget;
public:
rcstoken *get(int allow_eof);
void unget(rcstoken *token);
int eof()
{
return (input->gcount() == 0);
};
void match(const char *token)
{
auto_ptr<rcstoken> ptr(get(FALSE));
if (*ptr != token)
throw RCSExpected(ptr->data, token);
}
void match(const char c)
{
auto_ptr<rcstoken> token(get(FALSE));
if ((*token) != c)
throw RCSExpected(token->data, c);
};
TokenParser(istream *myinput)
{
input = myinput;
backget = NULL;
idx = 0;
input->read(buf, CHUNK_SIZE);
if ( (buflength = input->gcount()) == 0 )
throw RCSParseError("Non-existing file or empty file");
};
~TokenParser()
{
if (input != NULL)
{
delete input;
input = NULL;
};
if (backget != NULL)
{
delete backget;
backget = NULL;
};
};
};
/* this is the class that does the actual job: by reading each part of
the file and thus generate events to a sink event-handler*/
class tparseParser
{
private:
TokenParser *tokenstream;
Sink *sink;
void parse_rcs_admin();
void parse_rcs_tree();
void parse_rcs_description();
void parse_rcs_deltatext();
public:
tparseParser(istream *myinput, Sink* mysink)
{
sink = mysink;
tokenstream = new TokenParser(myinput);
}
void parse()
{
parse_rcs_admin();
parse_rcs_tree();
// many sinks want to know when the tree has been completed so they can
// do some work to prepare for the arrival of the deltatext
sink->tree_completed();
parse_rcs_description();
parse_rcs_deltatext();
// easiest for us to tell the sink it is done, rather than worry about
// higher level software doing it.
sink->parse_completed();
}
~tparseParser()
{
delete tokenstream;
delete sink;
}
};
#endif /* __PARSE_H */