/* * 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 : */