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