/*
* aegis - project change supervisor
* Copyright (C) 2001-2006, 2008, 2012, 2014 Peter Miller
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
%{
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
%}
%token ACCESS
%token AUTHOR
%token BRANCH
%token COLON
%token COMMENT
%token DATE
%token DESC
%token EXPAND
%token HEAD
%token IDENTIFIER
%token JUNK
%token LOCKS
%token LOG
%token NEXT
%token NUMBER
%token SEMI
%token STATE
%token STRICT
%token STRING
%token SUFFIX
%token SYMBOLS
%token TEXT
%union
{
nstring *lv_string;
nstring_list *lv_string_list;
}
%type NUMBER STRING IDENTIFIER date author next log state
%type strings_opt branch branch_opt identifiers_opt numbers_opt
%destructor { delete $$; }
%destructor { delete $$; }
%{
static symtab_ty *stp;
static format_version *head;
static nstring pfn;
static nstring lfn;
static format_version *
ancestor(format_version *fvp)
{
while (fvp->before)
fvp = fvp->before;
return fvp;
}
format_version *
rcs_parse(const nstring &physical, const nstring &logical)
{
extern int yyparse(void);
head = 0;
stp = new symtab_ty(5);
pfn = physical;
lfn = logical;
rcs_lex_open(physical);
yyparse();
rcs_lex_close();
delete stp;
stp = 0;
return ancestor(head);
}
static time_t
str2date(const nstring &s)
{
nstring_list sl;
sl.split(s, ".");
if (sl.size() != 6)
{
fatal_date_unknown(s.c_str());
}
// build a 'struct tm' value, to pass to mktime(3)
static struct tm tm_zero;
struct tm temp = tm_zero;
temp.tm_year = fix_tm_year(sl[0].to_long());
temp.tm_mon = sl[1].to_long() - 1;
temp.tm_mday = sl[2].to_long();
temp.tm_hour = sl[3].to_long();
temp.tm_min = sl[4].to_long();
temp.tm_sec = sl[5].to_long();
time_t t = mktime(&temp);
if (t == (time_t)-1)
{
fatal_date_unknown(s.c_str());
}
return t;
}
static format_version *
find(const nstring &edit)
{
assert(stp);
format_version *rp = (format_version *)stp->query(edit);
if (!rp)
{
rp = new format_version();
stp->assign(edit, rp);
rp->edit.push_back_unique(edit);
rp->filename_physical = pfn;
rp->filename_logical = lfn;
}
return rp;
}
static int
is_a_branch_version_number(const nstring &s)
{
return (s.count_the_dots() >= 2);
}
%}
%%
file
: admin tree desc edits
;
admin
: head branch_opt suffix_opt access symbols locks strict_opt
comment_opt expand_opt
{
delete $2;
}
;
head
: HEAD NUMBER SEMI
{
head = find(*$2);
$2 = NULL;
}
;
branch
: BRANCH numbers_opt SEMI
{ $$ = $2; }
;
branch_opt
: /* empty */
{ $$ = new nstring_list(); }
| branch
{ $$ = $1; }
;
suffix
: SUFFIX IDENTIFIER SEMI
{ (void)$2; }
| SUFFIX STRING SEMI
{ (void)$2; }
;
suffix_opt
: /* empty */
| suffix
;
access
: ACCESS identifiers_opt SEMI
{ (void)$2; }
;
symbols
: SYMBOLS symbol_list SEMI
;
symbol_list
: /* empty */
| symbol_list symbol
;
symbol
: IDENTIFIER COLON NUMBER
{
format_version *rp = find(*$3);
rp->tag.push_back(*$1);
}
;
locks
: LOCKS lock_list SEMI
;
lock_list
: /* empty */
| lock_list lock
;
lock
: IDENTIFIER COLON NUMBER
{
(void)$1;
(void)$3;
}
;
strict
: STRICT SEMI
;
strict_opt
: /* empty */
| strict
;
comment
: COMMENT STRING SEMI
{ (void)$2; }
;
comment_opt
: /* empty */
| comment
;
expand
: EXPAND strings_opt SEMI
{ delete $2; }
;
expand_opt
: /* empty */
| expand
;
identifiers_opt
: /* empty */
{
$$ = new nstring_list();
}
| identifiers_opt IDENTIFIER
{
$$ = $1;
$1 = NULL;
$$->push_back(*$2);
}
;
strings_opt
: /* empty */
{
$$ = new nstring_list();
}
| strings_opt STRING
{
$$ = $1;
$1 = NULL;
$$->push_back(*$2);
}
;
tree
: /* empty */
| tree delta
;
delta
: NUMBER date author state branch next
{
format_version *rp = find(*$1);
rp->when = str2date(*$2);
rp->who.push_back_unique(*$3);
/*
* The RCS manual says that
* "Exp" means experimental
* "Stab" means stable
* "Rel" means released
*
* CVS uses "dead" to indicate a file which has
* been deleted, and should not be checked out.
*/
rp->dead = (*$4 == "dead");
if ($5->size() && !rp->after_branch)
rp->after_branch = new format_version_list();
for (size_t j = 0; j < $5->size(); ++j)
{
format_version *other = find((*$5)[j]);
other->before = rp;
rp->after_branch->append(other);
}
if ($6)
{
/*
* The trunk is a roll-back lists, but
* the branches are roll-forward lists.
* (Sheesh!)
*/
if (is_a_branch_version_number(*$1))
{
rp->after = find(*$6);
rp->after->before = rp;
}
else
{
rp->before = find(*$6);
rp->before->after = rp;
}
}
}
;
date
: DATE NUMBER SEMI
{
$$ = $2;
$2 = NULL;
}
;
author
: AUTHOR IDENTIFIER SEMI
{
$$ = $2;
$2 = NULL;
}
;
state
: STATE IDENTIFIER SEMI
{
$$ = $2;
$2 = NULL;
}
;
numbers_opt
: /* empty */
{
$$ = new nstring_list();
}
| numbers_opt NUMBER
{
$$ = $1;
$1 = NULL;
$$->push_back(*$2);
}
;
next
: NEXT NUMBER SEMI
{
$$ = $2;
$2 = NULL;
}
| NEXT SEMI
{ $$ = NULL; }
;
desc
: DESC STRING nosemi
{ (void)$2; }
;
edits
: edit
| edits edit
;
edit
: NUMBER log text
{
format_version *rp = find(*$1);
nstring_list log;
log.split(*$2, "\n", true);
rp->description.push_back_unique(log);
}
;
log
: LOG STRING nosemi
{
$$ = $2;
$2 = NULL;
}
;
text
: TEXT STRING nosemi
{ (void)$2; }
;
nosemi
: /* empty */
{ rcs_lex_keyword_expected(); }
;
/* vim: set ts=8 sw=4 et : */