monotone

monotone Mtn Source Tree

Root/rcs_file.cc

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

Archive Download this file

Branches

Tags

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