307 lines
7.0 KiB
C++
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 */
|