/*
* aegis - project change supervisor
* Copyright (C) 2001-2006, 2008 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
%}
%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
{
string_ty *lv_string;
string_list_ty *lv_string_list;
}
%type NUMBER STRING IDENTIFIER date author next log state
%type strings_opt branch branch_opt identifiers_opt numbers_opt
%{
static symtab_ty *stp;
static format_version_ty *head;
static string_ty *pfn;
static string_ty *lfn;
static format_version_ty *
ancestor(format_version_ty *fvp)
{
while (fvp->before)
fvp = fvp->before;
return fvp;
}
format_version_ty *
rcs_parse(string_ty *physical, string_ty *logical)
{
extern int yyparse(void);
head = 0;
stp = symtab_alloc(5);
pfn = physical;
lfn = logical;
rcs_lex_open(physical);
yyparse();
rcs_lex_close();
symtab_free(stp);
stp = 0;
return ancestor(head);
}
static time_t
str2date(string_ty *s)
{
string_list_ty sl;
sl.split(s, ".");
if (sl.nstrings != 6)
{
fatal_date_unknown(s->str_text);
}
/*
* Turn it into a format that scan_date understands.
* (Probably not the fastest way to get is converted into a date.)
*/
string_ty *s2 =
str_format
(
"%s/%s/%s %s:%s:%s GMT",
sl.string[1]->str_text,
sl.string[2]->str_text,
sl.string[0]->str_text,
sl.string[3]->str_text,
sl.string[4]->str_text,
sl.string[5]->str_text
);
time_t t = date_scan(s2->str_text);
str_free(s2);
if (t == (time_t)-1)
{
fatal_date_unknown(s->str_text);
}
return t;
}
static format_version_ty *
find(string_ty *edit)
{
format_version_ty *rp;
assert(stp);
rp = (format_version_ty *)symtab_query(stp, edit);
if (!rp)
{
rp = new format_version_ty();
symtab_assign(stp, edit, rp);
rp->edit = str_copy(edit);
rp->filename_physical = str_copy(pfn);
rp->filename_logical = str_copy(lfn);
}
return rp;
}
static int
is_a_branch_version_number(string_ty *s)
{
const char *cp;
int count_the_dots;
count_the_dots = 0;
for (cp = s->str_text; *cp; ++cp)
{
if (*cp == '.')
{
++count_the_dots;
if (count_the_dots >= 2)
return 1;
}
}
return 0;
}
%}
%%
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);
str_free($2);
}
;
branch
: BRANCH numbers_opt SEMI
{ $$ = $2; }
;
branch_opt
: /* empty */
{ $$ = new string_list_ty(); }
| branch
{ $$ = $1; }
;
suffix
: SUFFIX IDENTIFIER SEMI
{ str_free($2); }
| SUFFIX STRING SEMI
{ str_free($2); }
;
suffix_opt
: /* empty */
| suffix
;
access
: ACCESS identifiers_opt SEMI
{ delete $2; }
;
symbols
: SYMBOLS symbol_list SEMI
;
symbol_list
: /* empty */
| symbol_list symbol
;
symbol
: IDENTIFIER COLON NUMBER
{
format_version_ty *rp;
rp = find($3);
rp->tag.push_back($1);
str_free($1);
str_free($3);
}
;
locks
: LOCKS lock_list SEMI
;
lock_list
: /* empty */
| lock_list lock
;
lock
: IDENTIFIER COLON NUMBER
{
str_free($1);
str_free($3);
}
;
strict
: STRICT SEMI
;
strict_opt
: /* empty */
| strict
;
comment
: COMMENT STRING SEMI
{ str_free($2); }
;
comment_opt
: /* empty */
| comment
;
expand
: EXPAND strings_opt SEMI
{ delete $2; }
;
expand_opt
: /* empty */
| expand
;
identifiers_opt
: /* empty */
{
$$ = new string_list_ty();
}
| identifiers_opt IDENTIFIER
{
$$ = $1;
$$->push_back($2);
str_free($2);
}
;
strings_opt
: /* empty */
{
$$ = new string_list_ty();
}
| strings_opt STRING
{
$$ = $1;
$$->push_back($2);
str_free($2);
}
;
tree
: /* empty */
| tree delta
;
delta
: NUMBER date author state branch next
{
size_t j;
format_version_ty *rp;
static string_ty *dead;
rp = find($1);
rp->when = str2date($2);
str_free($2);
rp->who = $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.
*/
if (!dead)
dead = str_from_c("dead");
rp->dead = str_equal($4, dead);
str_free($4);
if ($5->nstrings && !rp->after_branch)
rp->after_branch = format_version_list_new();
for (j = 0; j < $5->nstrings; ++j)
{
format_version_ty *other;
other = find($5->string[j]);
other->before = rp;
format_version_list_append
(
rp->after_branch,
other
);
}
delete $5;
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;
}
str_free($6);
}
}
;
date
: DATE NUMBER SEMI
{ $$ = $2; }
;
author
: AUTHOR IDENTIFIER SEMI
{ $$ = $2; }
;
state
: STATE IDENTIFIER SEMI
{ $$ = $2; }
;
numbers_opt
: /* empty */
{
$$ = new string_list_ty();
}
| numbers_opt NUMBER
{
$$ = $1;
$$->push_back($2);
str_free($2);
}
;
next
: NEXT NUMBER SEMI
{ $$ = $2; }
| NEXT SEMI
{ $$ = 0; }
;
desc
: DESC STRING nosemi
{ str_free($2); }
;
edits
: edit
| edits edit
;
edit
: NUMBER log text
{
format_version_ty *rp;
rp = find($1);
rp->description = $2;
}
;
log
: LOG STRING nosemi
{ $$ = $2; }
;
text
: TEXT STRING nosemi
{ str_free($2); }
;
nosemi
: /* empty */
{ rcs_lex_keyword_expected(); }
;