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 <fstream>
7#include <iostream>
8#include <string>
9#include <vector>
10
11#ifdef WIN32
12#include <windows.h>
13#endif
14
15#ifdef HAVE_MMAP
16#include <sys/mman.h>
17#endif
18
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <unistd.h>
22
23#ifdef HAVE_FCNTL
24#include <fcntl.h>
25#endif
26
27#include "config.h"
28#include "rcs_file.hh"
29#include "sanity.hh"
30
31
32#ifdef HAVE_MMAP
33struct
34file_handle
35{
36 std::string const & filename;
37 off_t length;
38 int fd;
39 file_handle(std::string const & fn) :
40 filename(fn),
41 length(0),
42 fd(-1)
43 {
44 struct stat st;
45 if (stat(fn.c_str(), &st) == -1)
46throw oops("stat of " + filename + " failed");
47 length = st.st_size;
48 fd = open(filename.c_str(), O_RDONLY);
49 if (fd == -1)
50throw oops("open of " + filename + " failed");
51 }
52 ~file_handle()
53 {
54 if (close(fd) == -1)
55throw oops("close of " + filename + " failed");
56 }
57};
58struct file_source
59{
60 std::string const & filename;
61 int fd;
62 off_t length;
63 off_t pos;
64 void * mapping;
65 bool good()
66 {
67 return pos < length;
68 }
69 int peek()
70 {
71 if (pos >= length)
72 return EOF;
73 else
74 return reinterpret_cast<char const *>(mapping)[pos];
75 }
76 bool get(char & c)
77 {
78 c = peek();
79 if (good())
80 ++pos;
81 return good();
82 }
83 file_source(std::string const & fn,
84 int f,
85 off_t len) :
86 filename(fn),
87 fd(f),
88 length(len),
89 pos(0),
90 mapping(NULL)
91 {
92 mapping = mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0);
93 if (mapping == MAP_FAILED)
94 throw oops("mmap of " + filename + " failed");
95 }
96 ~file_source()
97 {
98 if (munmap(mapping, length) == -1)
99 throw oops("munmapping " + filename + " failed, after reading RCS file");
100 }
101};
102#elif defined(WIN32)
103struct
104file_handle
105{
106 std::string const & filename;
107 off_t length;
108 HANDLE fd;
109 file_handle(std::string const & fn) :
110 filename(fn),
111 length(0),
112 fd(NULL)
113 {
114 struct stat st;
115 if (stat(fn.c_str(), &st) == -1)
116throw oops("stat of " + filename + " failed");
117 length = st.st_size;
118 fd = CreateFile(fn.c_str(),
119 GENERIC_READ,
120 FILE_SHARE_READ,
121 NULL,
122 OPEN_EXISTING, 0, NULL);
123 if (fd == NULL)
124throw oops("open of " + filename + " failed");
125 }
126 ~file_handle()
127 {
128 if (CloseHandle(fd)==0)
129throw oops("close of " + filename + " failed");
130 }
131};
132
133struct
134file_source
135{
136 std::string const & filename;
137 HANDLE fd,map;
138 off_t length;
139 off_t pos;
140 void * mapping;
141 bool good()
142 {
143 return pos < length;
144 }
145 int peek()
146 {
147 if (pos >= length)
148 return EOF;
149 else
150 return reinterpret_cast<char const *>(mapping)[pos];
151 }
152 bool get(char & c)
153 {
154 c = peek();
155 if (good())
156 ++pos;
157 return good();
158 }
159 file_source(std::string const & fn,
160 HANDLE f,
161 off_t len) :
162 filename(fn),
163 fd(f),
164 length(len),
165 pos(0),
166 mapping(NULL)
167 {
168 map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL);
169 if (map==NULL)
170 throw oops("CreateFileMapping of " + filename + " failed");
171 mapping = MapViewOfFile(map, FILE_MAP_READ, 0, 0, len);
172 if (mapping==NULL)
173 throw oops("MapViewOfFile of " + filename + " failed");
174 }
175 ~file_source()
176 {
177 if (UnmapViewOfFile(mapping)==0)
178 throw oops("UnmapViewOfFile of " + filename + " failed");
179 if (CloseHandle(map)==0)
180 throw oops("CloseHandle of " + filename + " failed");
181 }
182};
183#else
184// no mmap at all
185typedef std::istream file_source;
186#endif
187
188typedef enum
189 {
190 TOK_STRING,
191 TOK_SYMBOL,
192 TOK_NUM,
193 TOK_SEMI,
194 TOK_COLON,
195 TOK_NONE
196 }
197token_type;
198
199static token_type
200get_token(file_source & ist,
201 std::string & str)
202{
203 bool saw_idchar = false;
204 int i = ist.peek();
205 char c;
206 str.clear();
207
208 // eat leading whitespace
209 while (true)
210 {
211 if (i == EOF)
212return TOK_NONE;
213 if (!isspace(i))
214break;
215 ist.get(c);
216 i = ist.peek();
217 }
218
219 switch (i)
220 {
221 case ';':
222 ist.get(c);
223 return TOK_SEMI;
224 break;
225
226 case ':':
227 ist.get(c);
228 return TOK_COLON;
229 break;
230
231 case '@':
232 ist.get(c);
233 while (ist.get(c))
234{
235 if (c == '@')
236 {
237 if (ist.peek() == '@')
238{ ist.get(c); str += c; }
239 else
240break;
241 }
242 else
243 str += c;
244}
245 return TOK_STRING;
246 break;
247
248 default:
249 while (ist.good()
250 && i != ';'
251 && i != ':'
252 && !isspace(i))
253{
254 ist.get(c);
255 if (! isdigit(c) && c != '.')
256 saw_idchar = true;
257 str += c;
258 i = ist.peek();
259}
260 break;
261 }
262
263 if (str.empty())
264 return TOK_NONE;
265 else if (saw_idchar)
266 return TOK_SYMBOL;
267 else
268 return TOK_NUM;
269}
270
271struct parser
272{
273 file_source & ist;
274 rcs_file & r;
275 std::string token;
276 token_type ttype;
277
278 parser(file_source & s,
279 rcs_file & r)
280 : ist(s), r(r)
281 {}
282
283 std::string tt2str(token_type tt)
284 {
285 switch (tt)
286 {
287 case TOK_STRING:
288return "TOK_STRING";
289 case TOK_SYMBOL:
290return "TOK_SYMBOL";
291 case TOK_NUM:
292return "TOK_NUM";
293 case TOK_SEMI:
294return "TOK_SEMI";
295 case TOK_COLON:
296return "TOK_COLON";
297 case TOK_NONE:
298return "TOK_NONE";
299 }
300 return "TOK_UNKNOWN";
301 }
302
303 void advance()
304 {
305 ttype = get_token(ist, token);
306 // std::cerr << tt2str(ttype) << ": " << token << std::endl;
307 }
308
309 bool nump() { return ttype == TOK_NUM; }
310 bool strp() { return ttype == TOK_STRING; }
311 bool symp() { return ttype == TOK_SYMBOL; }
312 bool symp(std::string const & val)
313 {
314 return ttype == TOK_SYMBOL && token == val;
315 }
316 void eat(token_type want)
317 {
318 if (ttype != want)
319 throw oops("parse failure: expecting "
320 + tt2str(want)
321 + " got "
322 + tt2str(ttype)
323 + " with value: "
324 + token);
325 advance();
326 }
327
328 // basic "expect / extract" functions
329
330 void str(std::string & v) { v = token; eat(TOK_STRING); }
331 void str() { eat(TOK_STRING); }
332 void sym(std::string & v) { v = token; eat(TOK_SYMBOL); }
333 void sym() { eat(TOK_SYMBOL); }
334 void num(std::string & v) { v = token; eat(TOK_NUM); }
335 void num() { eat(TOK_NUM); }
336 void semi() { eat(TOK_SEMI); }
337 void colon() { eat(TOK_COLON); }
338 void expect(std::string const & expected)
339 {
340 std::string tmp;
341 if (!symp(expected))
342 throw oops(std::string("parse failure: ")
343 + "expecting word '"
344 + expected
345 + "'");
346 advance();
347 }
348
349 bool wordp()
350 {
351 return (ttype == TOK_STRING
352 || ttype == TOK_SYMBOL
353 || ttype == TOK_NUM
354 || ttype == TOK_COLON);
355 }
356 void word()
357 {
358 if (!wordp())
359 throw oops("expecting word");
360 advance();
361 }
362
363 void parse_newphrases(std::string const & terminator)
364 {
365 while(symp() && !symp(terminator))
366 {
367sym();
368while (wordp()) word();
369semi();
370 }
371 }
372
373 void parse_admin()
374 {
375 expect("head"); num(r.admin.head); semi();
376 if (symp("branch")) { sym(r.admin.branch); if (nump()) num(); semi(); }
377 expect("access"); while(symp()) { sym(); } semi();
378 expect("symbols");
379 while(symp())
380 {
381std::string stmp, ntmp;
382sym(stmp); colon(); num(ntmp);
383r.admin.symbols.insert(make_pair(ntmp, stmp));
384 }
385 semi();
386 expect("locks"); while(symp()) { sym(); colon(); num(); } semi();
387 if (symp("strict")) { sym(); semi(); }
388 if (symp("comment")) { sym(); if (strp()) { str(); } semi(); }
389 if (symp("expand")) { sym(); if (strp()) { str(); } semi(); }
390 parse_newphrases("");
391 }
392
393 void parse_deltas()
394 {
395 while (nump())
396 {
397rcs_delta d;
398num(d.num);
399expect("date"); num(d.date); semi();
400expect("author"); sym(d.author); semi();
401expect("state"); if (symp()) sym(d.state); semi();
402expect("branches");
403while(nump())
404 {
405 std::string tmp;
406 num(tmp);
407 d.branches.push_back(tmp);
408 }
409semi();
410expect("next"); if (nump()) num(d.next); semi();
411parse_newphrases("desc");
412r.push_delta(d);
413 }
414 }
415
416 void parse_desc()
417 {
418 expect("desc"); str();
419 }
420
421 void parse_deltatexts()
422 {
423 while(nump())
424 {
425rcs_deltatext d;
426num(d.num);
427expect("log"); str(d.log);
428parse_newphrases("text");
429expect("text"); str(d.text);
430r.push_deltatext(d);
431 }
432 }
433
434 void parse_file()
435 {
436 advance();
437 parse_admin();
438 parse_deltas();
439 parse_desc();
440 parse_deltatexts();
441 eat(TOK_NONE);
442 }
443};
444
445void
446parse_rcs_file(std::string const & filename, rcs_file & r)
447{
448#if defined(HAVE_MMAP) || defined(WIN32)
449 file_handle handle(filename);
450 file_source ifs(filename, handle.fd, handle.length);
451#else
452 std::ifstream ifs(filename.c_str());
453 ifs.unsetf(std::ios_base::skipws);
454#endif
455 parser p(ifs, r);
456 p.parse_file();
457}
458

Archive Download this file

Branches

Tags

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