/* * CvsGraph graphical representation generator of brances and revisions * of a file in cvs/rcs. * * Copyright (C) 2001 B. Stultiens * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ %{ #define YYERROR_VERBOSE 1 #include #include #include #include #include /* For gdFontPtr in cvsgraph.h */ #include "cvsgraph.h" #include "utils.h" #include "rcs.h" rcsfile_t *rcsfile; static tag_t *new_tag(char *s, char *r); static tags_t *new_tags(tag_t *s); static tags_t *add_tags(tags_t *s, tag_t *t); static idrev_t *new_idrev(char *s, char *r); static idrevs_t *new_idrevs(idrev_t *s); static idrevs_t *add_idrevs(idrevs_t *s, idrev_t *t); static ids_t *new_ids(char *s); static ids_t *add_ids(ids_t *s, char *t); static revs_t *new_revs(char *s); static revs_t *add_revs(revs_t *s, char *t); static dtext_t *new_dtext(char *n, char *l, char *t); static dtexts_t *add_dtexts(dtexts_t *s, dtext_t *t); static phrase_t *new_phrase(phrase_type_e pt, char *t); static phrases_t *add_phrases(phrases_t *s, phrase_t *t); static phrases_t *merge_phrases(phrases_t *s, phrases_t *t); static delta_t *new_delta(char *rev, char *date, char *author, char *state, revs_t *branches, char *next, phrases_t *phrases); static deltas_t *add_deltas(deltas_t *s, delta_t *t); static rcsfile_t *new_rcsfile(char *head, char *branch, ids_t *access, tags_t *tags, idrevs_t *locks, int strict, char *comment, char *expand); %} %union{ int i; char *str; tag_t *tag; tags_t *tags; idrev_t *idrev; idrevs_t *idrevs; ids_t *ids; revs_t *revs; dtext_t *dtext; dtexts_t *dtexts; delta_t *delta; deltas_t *deltas; rcsfile_t *rcsfile; phrases_t *phrases; phrase_t *phrase; } %token tHEAD tBRANCH tACCESS tSYMBOLS tLOCKS tSTRICT tCOMMENT %token tEXPAND tDATE tAUTHOR tSTATE tBRANCHES tNEXT %token tDESC tLOG tTEXT %token tOWNER tGROUP tPERMISSIONS tSPECIAL tSYMLINK tHARDLINKS %token tNAMESPACE tDEAD tMERGEPOINT %token tDELTATYPE tCOMMITID tKOPT tFILENAME tPROPERTIES %token tNEWPHRASE %token tSTRING tREV tID tSYM %type ostr orev oid obranch ocomment oexpand desc idorstr %type tag %type tags otags %type idrev %type idrevs oidrevs %type ids oids %type revs orevs %type ostrict %type dtext %type dtexts %type delta %type deltas %type admin %type ophrases %type phrase %% rcsfile : admin deltas desc { rcsfile = $1; rcsfile->deltas = $2; rcsfile->desc = $3; rcsfile->dtexts = NULL; if(rcsfile->head) { char *h = xstrdup("HEAD"); char *r = xstrdup(rcsfile->head->rev); tag_t *t = new_tag(h, r); t->ignore = -1; /* Make sure it doesn't get zapped */ rcsfile->tags = add_tags(rcsfile->tags, t); } if(rcsfile->branch) { char *h = xstrdup("MAIN"); char *r = xstrdup(rcsfile->branch->rev); tag_t *t = new_tag(h, r); t->rev->isbranch = 1; rcsfile->tags = add_tags(rcsfile->tags, t); } /* Else we need to add the main tag later because the * trunk is reversed in order and the primary revision * numbers can vary. */ if(!conf.parse_logs) { /* Return from the yyparse() call early to avoid parsing * of the possibly extremely large deltatext. This speeds * up parsing of binary files by a very large factor. */ return 0; } } /* This is trailing deltatext context and possibly ignored */ dtexts { rcsfile->dtexts = $5; } ; admin : tHEAD orev ';' obranch tACCESS { set_id(); } oids ';' tSYMBOLS { set_sym(); } otags ';' tLOCKS { set_id(); } oidrevs ';' ostrict ocomment oexpand ophrases { $$ = new_rcsfile($2, $4, $7, $11, $15, $17, $18, $19); } ; ostrict : /* Empty */ { $$ = 0; } | tSTRICT ';' { $$ = 1; } ; obranch : /* Empty */ { $$ = NULL; } | tBRANCH orev ';' { $$ = $2; } ; ocomment: /* Empty */ { $$ = NULL; } | tCOMMENT ostr ';' { $$ = $2; } ; oexpand : /* Empty */ { $$ = NULL; } | tEXPAND ostr ';' { $$ = $2; } ; deltas : /* Empty */ { $$ = NULL; } | deltas delta { $$ = add_deltas($1, $2); } ; delta : tREV tDATE tREV ';' tAUTHOR { set_author(); } idorstr ';' tSTATE { set_id(); } oid ';' tBRANCHES orevs ';' ophrases tNEXT orev ';' ophrases { $$ = new_delta($1, $3, $7, $11, $14, $18, merge_phrases($16, $20)); } ; idorstr : tID { $$ = $1; } | tSTRING { $$ = $1; } ; desc : tDESC tSTRING { $$ = $2; } ; dtexts : /* Empty */ { $$ = NULL; } | dtexts dtext { $$ = add_dtexts($1, $2); } ; dtext : tREV tLOG tSTRING ophrases tTEXT { set_skipstr(); } tSTRING { $$ = new_dtext($1, $3, $7); } ; ophrases: /* Empty */ { $$ = NULL; } | ophrases phrase { $$ = add_phrases($1, $2); } ; phrase : tNEWPHRASE { set_skip(); } ';' { yywarning("Unrecognised `newphrase´ keyword '%s' skipped", $1); xfree($1); $$ = NULL; } | cvskw { set_skip(); } ';' { $$ = NULL; /* yywarning("CVS extended keyword skipped"); */ } | otherkw { set_skip(); } ';' { $$ = NULL; /* yywarning("Other extended keyword"); */ } | tMERGEPOINT tSTRING ';' { $$ = new_phrase(PT_MERGEPOINT, $2); } | tMERGEPOINT tREV ';' { $$ = new_phrase(PT_MERGEPOINT, $2); } ; cvskw : tOWNER | tGROUP | tPERMISSIONS | tSPECIAL | tSYMLINK | tHARDLINKS ; otherkw : tNAMESPACE | tDEAD | tDELTATYPE | tCOMMITID | tKOPT | tFILENAME | tPROPERTIES ; ostr : /* Empty */ { $$ = NULL; } | tSTRING { $$ = $1; } ; orev : /* Empty */ { $$ = NULL; } | tREV { $$ = $1; } ; orevs : /* Empty */ { $$ = NULL; } | revs { $$ = $1; } ; revs : tREV { $$ = new_revs($1); } | revs tREV { $$ = add_revs($1, $2); } ; oid : /* Empty */ { $$ = NULL; } | tID { $$ = $1; } ; oids : /* Empty */ { $$ = NULL; } | ids { $$ = $1; } ; ids : tID { set_id(); $$ = new_ids($1); } | ids tID { set_id(); $$ = add_ids($1, $2); } ; oidrevs : /* Empty */ { $$ = NULL; } | idrevs { $$ = $1; } ; idrevs : idrev { $$ = new_idrevs($1); } | idrevs idrev { $$ = add_idrevs($1, $2); } ; idrev : tID ':' tREV { set_id(); $$ = new_idrev($1, $3); } ; otags : /* Empty */ { $$ = NULL; } | tags { $$ = $1; } ; tags : tag { if($1) $$ = new_tags($1); else $$ = NULL; } | tags tag { if($1 && $2) $$ = add_tags($1, $2); else if($2) $$ = new_tags($2); else $$ = $1; } ; tag : tSYM ':' tREV { set_sym(); $$ = new_tag($1, $3); } /* Zap the additional tag-info from CVSNT */ /* This is a bit of a hack, but it is necessary to do like this */ /* because the parser is not allowed to do any look-ahead on ':'. */ /* Otherwise, we would have a tNEWPHRASE hit because we cannot set */ /* set_sym() before the lexer gets the next token. */ | ':' tREV ':' tSTRING { set_sym(); $$ = NULL; } ; %% static rev_t *make_rev(char *s, int isrev) { char *cptr; int dots = 0; rev_t *r; if(!s) return NULL; r = xmalloc(sizeof(*r)); for(cptr = s; *cptr; cptr++) { if(*cptr == '.') dots++; } if(!dots) { r->rev = xstrdup(""); r->branch = xstrdup(s); r->isbranch = 1; } else if(!*s) { r->rev = xstrdup("?.?"); r->branch = xstrdup("?"); } else if(dots & 1) { char *t; r->rev = s; r->branch = xstrdup(s); cptr = strrchr(r->branch, '.'); assert(cptr != NULL); *cptr = '\0'; t = strrchr(r->branch, '.'); if(!isrev && ((t && !strcmp(t+1, "0")) || (!t && !strcmp(r->branch, "0")))) { /* Magic branch numbers "x.x.0.x" */ r->isbranch = 1; if(t) strcpy(t+1, cptr+1); else strcpy(r->branch, cptr+1); } } else { r->isbranch = 1; r->branch = s; r->rev = xmalloc(strlen(s) + 3); strcpy(r->rev, s); strcat(r->rev, ".?"); } return r; } static tag_t *new_tag(char *s, char *r) { tag_t *t = xmalloc(sizeof(*t)); t->tag = s; t->rev = make_rev(r, 0); return t; } static tags_t *new_tags(tag_t *s) { tags_t *t = xmalloc(sizeof(*t)); t->ntags = 1; t->tags = xmalloc(sizeof(t->tags[0])); t->tags[0] = s; return t; } static tags_t *add_tags(tags_t *s, tag_t *t) { if(!s) return new_tags(t); s->tags = xrealloc(s->tags, (s->ntags+1) * sizeof(s->tags[0])); s->tags[s->ntags] = t; s->ntags++; return s; } static idrev_t *new_idrev(char *s, char *r) { idrev_t *t = xmalloc(sizeof(*t)); t->id = s; t->rev = make_rev(r, 1); return t; } static idrevs_t *new_idrevs(idrev_t *s) { idrevs_t *t = xmalloc(sizeof(*t)); t->nidrevs = 1; t->idrevs = xmalloc(sizeof(t->idrevs[0])); t->idrevs[0] = s; return t; } static idrevs_t *add_idrevs(idrevs_t *s, idrev_t *t) { if(!s) return new_idrevs(t); s->idrevs = xrealloc(s->idrevs, (s->nidrevs+1) * sizeof(s->idrevs[0])); s->idrevs[s->nidrevs] = t; s->nidrevs++; return s; } static ids_t *new_ids(char *s) { ids_t *t = xmalloc(sizeof(*t)); t->nids = 1; t->ids = xmalloc(sizeof(t->ids[0])); t->ids[0] = s; return t; } static ids_t *add_ids(ids_t *s, char *t) { s->ids = xrealloc(s->ids, (s->nids+1) * sizeof(s->ids[0])); s->ids[s->nids] = t; s->nids++; return s; } static revs_t *new_revs(char *s) { revs_t *t = xmalloc(sizeof(*t)); t->nrevs = 1; t->revs = xmalloc(sizeof(t->revs[0])); t->revs[0] = make_rev(s, 1); return t; } static revs_t *add_revs(revs_t *s, char *t) { s->revs = xrealloc(s->revs, (s->nrevs+1) * sizeof(s->revs[0])); s->revs[s->nrevs] = make_rev(t, 1); s->nrevs++; return s; } static dtext_t *new_dtext(char *n, char *l, char *t) { dtext_t *d = xmalloc(sizeof(*d)); d->rev = make_rev(n, 1); d->log = l; d->text = t; return d; } static dtexts_t *add_dtexts(dtexts_t *s, dtext_t *t) { if(!s) s = xmalloc(sizeof(*s)); s->dtexts = xrealloc(s->dtexts, (s->ndtexts+1) * sizeof(s->dtexts[0])); s->dtexts[s->ndtexts] = t; s->ndtexts++; return s; } static phrase_t *new_phrase(phrase_type_e pt, char *t) { phrase_t *d = xmalloc(sizeof(*d)); d->type = pt; d->rev = make_rev(t, 1); return d; } static phrases_t *add_phrases(phrases_t *s, phrase_t *t) { if(!t) return s; if(!s) s = xmalloc(sizeof(*s)); if(t) { s->phrases = xrealloc(s->phrases, (s->nphrases + 1) * sizeof(s->phrases[0])); s->phrases[s->nphrases] = t; s->nphrases++; } return s; } static phrases_t *merge_phrases(phrases_t *s, phrases_t *t) { if(!t) return s; if(!s) return t; s->phrases = xrealloc(s->phrases, (s->nphrases + t->nphrases) * sizeof(s->phrases[0])); memcpy(&s->phrases[s->nphrases], &t->phrases[0], t->nphrases * sizeof(t->phrases[0])); s->nphrases += t->nphrases; /* FIXME: Free t and content; hm, who cares... */ return s; } static phrase_t *find_mergepoint(phrases_t *p) { int i; if(!p) return NULL; for(i = 0; i < p->nphrases; i++) { if(p->phrases[i]->type == PT_MERGEPOINT) return p->phrases[i]; } return NULL; } static delta_t *new_delta(char *rev, char *date, char *author, char *state, revs_t *branches, char *next, phrases_t *phrases) { delta_t *d = xmalloc(sizeof(*d)); d->rev = make_rev(rev, 1); d->date = date; d->author = author; d->state = state; d->branches = branches; d->next = make_rev(next, 1); d->phrases = phrases; d->mergepoint = find_mergepoint(phrases); return d; } static deltas_t *add_deltas(deltas_t *s, delta_t *t) { if(!s) s = xmalloc(sizeof(*s)); s->deltas = xrealloc(s->deltas, (s->ndeltas+1) * sizeof(s->deltas[0])); s->deltas[s->ndeltas] = t; s->ndeltas++; return s; } static rcsfile_t *new_rcsfile(char *head, char *branch, ids_t *access, tags_t *tags, idrevs_t *locks, int strict, char *comment, char *expand) { rcsfile_t *r = xmalloc(sizeof(*r)); r->head = make_rev(head, 1); r->branch = make_rev(branch, 0); r->access = access; r->tags = tags; r->locks = locks; r->strict = strict; r->comment = comment; r->expand = expand; return r; }