monotone

monotone Mtn Source Tree

Root/rcs_file.cc

1// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
2// all rights reserved.
3// licensed to the public under the terms of the GNU GPL (>= 2)
4// see the file COPYING for details
5
6#include <boost/lexical_cast.hpp>
7#include <boost/spirit.hpp>
8#include <boost/spirit/attribute.hpp>
9#include <boost/spirit/phoenix/binders.hpp>
10
11#include <sys/mman.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <unistd.h>
15
16#include "rcs_file.hh"
17#include "sanity.hh"
18
19using namespace std;
20using namespace boost::spirit;
21using boost::lexical_cast;
22using namespace phoenix;
23
24
25struct rcs_admin_closure :
26 boost::spirit::closure<rcs_admin_closure, rcs_admin>
27{ member1 val; };
28
29struct rcs_delta_closure :
30 boost::spirit::closure<rcs_delta_closure, rcs_delta>
31{ member1 val; };
32
33struct rcs_symbol_closure :
34 boost::spirit::closure<rcs_symbol_closure, rcs_symbol>
35{ member1 val; };
36
37struct rcs_desc_closure :
38 boost::spirit::closure<rcs_desc_closure, string>
39{ member1 val; };
40
41struct rcs_deltatext_closure :
42 boost::spirit::closure<rcs_deltatext_closure, rcs_deltatext>
43{ member1 val; };
44
45struct rcs_file_closure :
46 boost::spirit::closure<rcs_file_closure, rcs_file>
47{ member1 val; };
48
49struct string_closure :
50 boost::spirit::closure<string_closure, string>
51{ member1 val; };
52
53
54string unescape_string(char const * ch, char const * end)
55{
56 static string str;
57 str.reserve(static_cast<int>(end - ch));
58 str.clear();
59 if (*ch != '@')
60 throw oops("parser reported string without leading @");
61 while(++ch < end)
62 {
63 if (*ch == '@')
64{
65 if (ch + 1 == end)
66 break;
67 if (*(++ch) != '@')
68 throw oops("parser reported a single @ before end of string");
69}
70 str += *ch;
71 }
72 return string(str);
73}
74
75struct rcsfile_grammar :
76 public grammar<rcsfile_grammar, rcs_file_closure::context_t>
77{
78 template <typename ScannerT>
79 struct definition
80 {
81
82 // phrases with closures
83 subrule<0, rcs_file_closure ::context_t> rcstext;
84 subrule<1, rcs_admin_closure ::context_t> admin;
85 subrule<2, rcs_symbol_closure ::context_t> symbol;
86 subrule<3, rcs_desc_closure ::context_t> desc;
87 subrule<4, rcs_delta_closure ::context_t> delta;
88 subrule<5, rcs_deltatext_closure ::context_t> deltatext;
89
90 // tokens we want to record in strings
91 subrule<6, string_closure ::context_t> str;
92 subrule<7, string_closure ::context_t> sym;
93 subrule<8, string_closure ::context_t> num;
94 subrule<9, string_closure ::context_t> id;
95 subrule<10, string_closure ::context_t> word;
96
97 // non-recorded tokens
98 subrule<11> semi;
99 subrule<12> col;
100 subrule<13> newphrase;
101
102 // lexemes
103 subrule<14> idchar;
104 subrule<15> numchs;
105
106 // the root node
107 rule<ScannerT> top;
108 rule<ScannerT> const &
109 start() const { return top; }
110
111 definition(rcsfile_grammar const & self)
112 {
113
114 top =
115(
116 rcstext =
117 (admin[ bind(&rcs_file::admin)(self.val) = arg1 ]
118 >> (* (delta[bind(&rcs_file::push_delta)(self.val,arg1)])
119 | (*newphrase >>
120 *(delta[bind(&rcs_file::push_delta)(self.val,arg1)]) ))
121 >> ( desc | (*newphrase >> desc ))
122 >> *(deltatext[bind(&rcs_file::push_deltatext)(self.val,arg1)])
123 >> lexeme_d[*ch_p('\n')]),
124
125 admin =
126 str_p("head") >> num [bind(&rcs_admin::head )(admin.val) = arg1] >> semi >>
127 !(str_p("branch") >> num [bind(&rcs_admin::branch )(admin.val) = arg1] >> semi) >>
128 str_p("access") >> *(id [bind(&rcs_admin::access )(admin.val) = arg1]) >> semi >>
129 str_p("symbols") >> *(symbol [bind(&rcs_admin::push_symbol)(admin.val,arg1)]) >> semi >>
130 str_p("locks") >> *(id >> col >> num) >> semi >>
131 !(str_p("strict") >> semi) >>
132 !(str_p("comment") >> str [bind(&rcs_admin::comment )(admin.val) = arg1] >> semi) >>
133 !(str_p("expand") >> str [bind(&rcs_admin::expand )(admin.val) = arg1] >> semi),
134
135 symbol = sym[bind(&rcs_symbol::name )(symbol.val) = arg1] >> col
136 >> num[bind(&rcs_symbol::version)(symbol.val) = arg1],
137
138 delta =
139 num [bind(&rcs_delta::num) (delta.val) = arg1] >>
140 str_p("date") >> num [bind(&rcs_delta::date) (delta.val) = arg1] >> semi >>
141 str_p("author") >> id [bind(&rcs_delta::author)(delta.val) = arg1] >> semi >>
142 str_p("state") >> !(id [bind(&rcs_delta::state) (delta.val) = arg1]) >> semi >>
143 str_p("branches") >> *(num[bind(&rcs_delta::push_branch)(delta.val,arg1)]) >> semi >>
144 str_p("next") >> !(num[bind(&rcs_delta::next) (delta.val) = arg1]) >> semi,
145
146 desc = str_p("desc") >> str[desc.val = arg1 ],
147
148 deltatext =
149 num [bind(&rcs_deltatext::num) (deltatext.val) = arg1 ] >>
150 str_p("log") >> str [bind(&rcs_deltatext::log) (deltatext.val) = arg1 ] >>
151 (str_p("text") | (*newphrase >> str_p("text"))) >>
152 str [bind(&rcs_deltatext::text) (deltatext.val) = arg1 ]
153 ,
154
155 // phrase-level phrase rule definitions:
156 word = id | num | str | semi,
157 newphrase = id >> *word >> semi,
158
159 // these are to be called as character-level rules (in lexeme_d[...] blocks)
160 idchar = graph_p - chset<>("@,.:;@"),
161 numchs = +(digit_p | ch_p('.')),
162
163 // these are lexical units which can be called from outside lexeme_d[...] blocks
164 semi = ch_p(';'),
165 col = ch_p(':'),
166
167 sym = lexeme_d[ *digit_p >> idchar >> *(idchar | digit_p) ]
168 [ sym.val = construct_<string>(arg1,arg2) ],
169
170 num = lexeme_d[ numchs ]
171 [ num.val = construct_<string>(arg1,arg2) ],
172
173 id = lexeme_d[ !numchs >> idchar >> *(idchar | numchs) ]
174 [ id.val = construct_<string>(arg1,arg2) ],
175
176 str = lexeme_d[ ch_p('@')
177 >>
178 *((chset<>(anychar_p) - chset<>('@'))
179 | (ch_p('@') >> ch_p('@')))
180 >>
181 ch_p('@') ]
182 [ str.val = bind(&unescape_string)(arg1,arg2) ]
183 );
184 }
185 };
186};
187
188struct file_handle
189{
190 string const & filename;
191 off_t length;
192 int fd;
193 file_handle(string const & fn) :
194 filename(fn),
195 length(0),
196 fd(-1)
197 {
198 struct stat st;
199 if (stat(fn.c_str(), &st) == -1)
200throw oops("stat of " + filename + " failed");
201 length = st.st_size;
202 fd = open(filename.c_str(), O_RDONLY);
203 if (fd == -1)
204throw oops("open of " + filename + " failed");
205 }
206 ~file_handle()
207 {
208 if (close(fd) == -1)
209throw oops("close of " + filename + " failed");
210 }
211};
212
213struct mmapped_handle
214{
215 string const & filename;
216 int fd;
217 off_t length;
218 void * mapping;
219 mmapped_handle(string const & fn,
220int f,
221off_t len) :
222 filename(fn),
223 fd(f),
224 length(len),
225 mapping(NULL)
226 {
227 mapping = mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0);
228 if (mapping == MAP_FAILED)
229throw oops("mmap of " + filename + " failed");
230 }
231 ~mmapped_handle()
232 {
233 if (munmap(mapping, length) == -1)
234throw oops("munmapping " + filename + " failed, after reading RCS file");
235 }
236};
237
238void parse_rcs_file(string const & filename, rcs_file & r)
239{
240
241 file_handle file(filename);
242 mmapped_handle mm(filename, file.fd, file.length);
243
244 char const * begin = reinterpret_cast<char const *>(mm.mapping);
245 char const * end = begin + file.length;
246
247 rcsfile_grammar gram;
248 rcs_file rcs;
249 parse_info<char const *> info =
250 parse(begin, end,
251 gram[var(rcs) = arg1],
252 space_p);
253
254 if (info.hit && info.full)
255 r = rcs;
256 else
257 throw oops( "parse of RCS file " + filename + " failed"
258+ ", info.hit = " + lexical_cast<string>(info.hit)
259+ ", info.full = " + lexical_cast<string>(info.full)
260+ ", stopped at = " + lexical_cast<string>(info.stop - begin));
261}
262

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status