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)
46 throw oops("stat of " + filename + " failed");
47 length = st.st_size;
48 fd = open(filename.c_str(), O_RDONLY);
49 if (fd == -1)
50 throw oops("open of " + filename + " failed");
51 }
52 ~file_handle()
53 {
54 if (close(fd) == -1)
55 throw 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)
116 throw 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)
124 throw oops("open of " + filename + " failed");
125 }
126 ~file_handle()
127 {
128 if (CloseHandle(fd)==0)
129 throw 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 inline void
200adv(char i, size_t & line, size_t & col)
201{
202 if (i == '\n')
203 {
204 col = 0;
205 ++line;
206 }
207 else
208 ++col;
209}
210
211static token_type
212get_token(file_source & ist,
213 std::string & str,
214 size_t & line,
215 size_t & col)
216{
217 bool saw_idchar = false;
218 int i = ist.peek();
219 char c;
220 str.clear();
221
222 // eat leading whitespace
223 while (true)
224 {
225 if (i == EOF)
226 return TOK_NONE;
227 adv(i, line, col);
228 if (!isspace(i))
229 break;
230 ist.get(c);
231 i = ist.peek();
232 }
233
234 switch (i)
235 {
236 case ';':
237 ist.get(c);
238 ++col;
239 return TOK_SEMI;
240 break;
241
242 case ':':
243 ist.get(c);
244 ++col;
245 return TOK_COLON;
246 break;
247
248 case '@':
249 ist.get(c);
250 ++col;
251 while (ist.get(c))
252 {
253 if (c == '@')
254 {
255 if (ist.peek() == '@')
256 { ist.get(c); str += c; ++col; }
257 else
258 break;
259 }
260 else
261 {
262 adv(i, line, col);
263 str += c;
264 }
265 }
266 return TOK_STRING;
267 break;
268
269 default:
270 while (ist.good()
271 && i != ';'
272 && i != ':'
273 && !isspace(i))
274 {
275 ist.get(c);
276 ++col;
277 if (! isdigit(c) && c != '.')
278 saw_idchar = true;
279 str += c;
280 i = ist.peek();
281 }
282 break;
283 }
284
285 if (str.empty())
286 return TOK_NONE;
287 else if (saw_idchar)
288 return TOK_SYMBOL;
289 else
290 return TOK_NUM;
291}
292
293struct parser
294{
295 file_source & ist;
296 rcs_file & r;
297 std::string token;
298 token_type ttype;
299
300 size_t line, col;
301
302 parser(file_source & s,
303 rcs_file & r)
304 : ist(s), r(r), line(1), col(1)
305 {}
306
307 std::string tt2str(token_type tt)
308 {
309 switch (tt)
310 {
311 case TOK_STRING:
312 return "TOK_STRING";
313 case TOK_SYMBOL:
314 return "TOK_SYMBOL";
315 case TOK_NUM:
316 return "TOK_NUM";
317 case TOK_SEMI:
318 return "TOK_SEMI";
319 case TOK_COLON:
320 return "TOK_COLON";
321 case TOK_NONE:
322 return "TOK_NONE";
323 }
324 return "TOK_UNKNOWN";
325 }
326
327 void advance()
328 {
329 ttype = get_token(ist, token, line, col);
330 // std::cerr << tt2str(ttype) << ": " << token << std::endl;
331 }
332
333 bool nump() { return ttype == TOK_NUM; }
334 bool strp() { return ttype == TOK_STRING; }
335 bool symp() { return ttype == TOK_SYMBOL; }
336 bool symp(std::string const & val)
337 {
338 return ttype == TOK_SYMBOL && token == val;
339 }
340 void eat(token_type want)
341 {
342 if (ttype != want)
343 throw oops((F("parse failure %d:%d: expecting %s, got %s with value '%s'\n")
344 % line % col % tt2str(want) % tt2str(ttype) % token).str());
345 advance();
346 }
347
348 // basic "expect / extract" functions
349
350 void str(std::string & v) { v = token; eat(TOK_STRING); }
351 void str() { eat(TOK_STRING); }
352 void sym(std::string & v) { v = token; eat(TOK_SYMBOL); }
353 void sym() { eat(TOK_SYMBOL); }
354 void num(std::string & v) { v = token; eat(TOK_NUM); }
355 void num() { eat(TOK_NUM); }
356 void semi() { eat(TOK_SEMI); }
357 void colon() { eat(TOK_COLON); }
358 void expect(std::string const & expected)
359 {
360 std::string tmp;
361 if (!symp(expected))
362 throw oops((F("parse failure %d:%d: expecting word '%s'\n")
363 % line % col % expected).str());
364 advance();
365 }
366
367 bool wordp()
368 {
369 return (ttype == TOK_STRING
370 || ttype == TOK_SYMBOL
371 || ttype == TOK_NUM
372 || ttype == TOK_COLON);
373 }
374 void word()
375 {
376 if (!wordp())
377 throw oops((F("parse failure %d:%d: expecting word\n")
378 % line % col).str());
379 advance();
380 }
381
382 void parse_newphrases(std::string const & terminator)
383 {
384 while(symp() && !symp(terminator))
385 {
386 sym();
387 while (wordp()) word();
388 semi();
389 }
390 }
391
392 void parse_admin()
393 {
394 expect("head"); num(r.admin.head); semi();
395 if (symp("branch")) { sym(r.admin.branch); if (nump()) num(); semi(); }
396 expect("access"); while(symp()) { sym(); } semi();
397 expect("symbols");
398
399 // "man rcsfile" lies: there are real files in the wild which use
400 // num tokens as the key value in a symbols entry. for example
401 // "3.1:1.1.0.2" is a real sym:num specification, despite "3.1"
402 // being a num itself, not a sym.
403
404 while(symp() || nump())
405 {
406 std::string stmp, ntmp;
407if (symp())
408 {
409 sym(stmp); colon(); num(ntmp);
410 }
411else
412 {
413 num(stmp); colon(); num(ntmp);
414 }
415 r.admin.symbols.insert(make_pair(ntmp, stmp));
416 }
417 semi();
418 expect("locks"); while(symp()) { sym(); colon(); num(); } semi();
419 if (symp("strict")) { sym(); semi(); }
420 if (symp("comment")) { sym(); if (strp()) { str(); } semi(); }
421 if (symp("expand")) { sym(); if (strp()) { str(); } semi(); }
422 parse_newphrases("");
423 }
424
425 void parse_deltas()
426 {
427 while (nump())
428 {
429 rcs_delta d;
430 num(d.num);
431 expect("date"); num(d.date); semi();
432 expect("author"); sym(d.author); semi();
433 expect("state"); if (symp()) sym(d.state); semi();
434 expect("branches");
435 while(nump())
436 {
437 std::string tmp;
438 num(tmp);
439 d.branches.push_back(tmp);
440 }
441 semi();
442 expect("next"); if (nump()) num(d.next); semi();
443 parse_newphrases("desc");
444 r.push_delta(d);
445 }
446 }
447
448 void parse_desc()
449 {
450 expect("desc"); str();
451 }
452
453 void parse_deltatexts()
454 {
455 while(nump())
456 {
457 rcs_deltatext d;
458 num(d.num);
459 expect("log"); str(d.log);
460 parse_newphrases("text");
461 expect("text"); str(d.text);
462 r.push_deltatext(d);
463 }
464 }
465
466 void parse_file()
467 {
468 advance();
469 parse_admin();
470 parse_deltas();
471 parse_desc();
472 parse_deltatexts();
473 eat(TOK_NONE);
474 }
475};
476
477void
478parse_rcs_file(std::string const & filename, rcs_file & r)
479{
480#if defined(HAVE_MMAP) || defined(WIN32)
481 file_handle handle(filename);
482 file_source ifs(filename, handle.fd, handle.length);
483#else
484 std::ifstream ifs(filename.c_str());
485 ifs.unsetf(std::ios_base::skipws);
486#endif
487 parser p(ifs, r);
488 p.parse_file();
489}
490

Archive Download this file

Branches

Tags

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