| /* |
| # Copyright (C) 1999-2023 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 */ |