/* # 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 # # 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 /* for auto_ptr */ #include /* for iterator */ #include /* for exception */ #include /* for istream */ #include /* for list<> */ #include /* 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 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 ptr(get(FALSE)); if (*ptr != token) throw RCSExpected(ptr->data, token); } void match(const char c) { auto_ptr 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 */