#include "rcsbase.h"
libId(revId, "$Id: rcsrev.c,v 1.1.1.1 1999/04/23 01:43:39 wsanchez Exp $")
static char const *branchtip P((char const*));
static char const *lookupsym P((char const*));
static char const *normalizeyear P((char const*,char[5]));
static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**));
static void absent P((char const*,int));
static void cantfindbranch P((char const*,char const[datesize],char const*,char const*));
static void store1 P((struct hshentries***,struct hshentry*));
int
countnumflds(s)
char const *s;
{
register char const *sp;
register int count;
if (!(sp=s) || !*sp)
return 0;
count = 1;
do {
if (*sp++ == '.') count++;
} while (*sp);
return(count);
}
void
getbranchno(revno,branchno)
char const *revno;
struct buf *branchno;
{
register int numflds;
register char *tp;
bufscpy(branchno, revno);
numflds=countnumflds(revno);
if (!(numflds & 1)) {
tp = branchno->string;
while (--numflds)
while (*tp++ != '.')
continue;
*(tp-1)='\0';
}
}
int cmpnum(num1, num2)
char const *num1, *num2;
{
register char const *s1, *s2;
register size_t d1, d2;
register int r;
s1 = num1 ? num1 : "";
s2 = num2 ? num2 : "";
for (;;) {
if (!*s1)
return (unsigned char)*s2;
if (!*s2)
return -1;
while (*s1=='0') ++s1;
while (*s2=='0') ++s2;
for (d1=0; isdigit(*(s1+d1)); d1++) continue;
for (d2=0; isdigit(*(s2+d2)); d2++) continue;
if (d1 != d2)
return d1<d2 ? -1 : 1;
if ((r = memcmp(s1, s2, d1)))
return r;
s1 += d1;
s2 += d1;
if (*s1) s1++;
if (*s2) s2++;
}
}
int cmpnumfld(num1, num2, fld)
char const *num1, *num2;
int fld;
{
register char const *s1, *s2;
register size_t d1, d2;
s1 = num1;
s2 = num2;
while (--fld) {
while (*s1++ != '.')
continue;
while (*s2++ != '.')
continue;
}
while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1;
}
int
cmpdate(d1, d2)
char const *d1, *d2;
{
char year1[5], year2[5];
int r = cmpnumfld(normalizeyear(d1,year1), normalizeyear(d2,year2), 1);
if (r)
return r;
else {
while (isdigit(*d1)) d1++; d1 += *d1=='.';
while (isdigit(*d2)) d2++; d2 += *d2=='.';
return cmpnum(d1, d2);
}
}
static char const *
normalizeyear(date, year)
char const *date;
char year[5];
{
if (isdigit(date[0]) && isdigit(date[1]) && !isdigit(date[2])) {
year[0] = '1';
year[1] = '9';
year[2] = date[0];
year[3] = date[1];
year[4] = 0;
return year;
} else
return date;
}
static void
cantfindbranch(revno, date, author, state)
char const *revno, date[datesize], *author, *state;
{
char datebuf[datesize + zonelenmax];
rcserror("No revision on branch %s has%s%s%s%s%s%s.",
revno,
date ? " a date before " : "",
date ? date2str(date,datebuf) : "",
author ? " and author "+(date?0:4) : "",
author ? author : "",
state ? " and state "+(date||author?0:4) : "",
state ? state : ""
);
}
static void
absent(revno, field)
char const *revno;
int field;
{
struct buf t;
bufautobegin(&t);
rcserror("%s %s absent", field&1?"revision":"branch",
partialno(&t,revno,field)
);
bufautoend(&t);
}
int
compartial(num1, num2, length)
char const *num1, *num2;
int length;
{
register char const *s1, *s2;
register size_t d1, d2;
register int r;
s1 = num1; s2 = num2;
if (!s1) return 1;
if (!s2) return -1;
for (;;) {
if (!*s1) return 1;
if (!*s2) return -1;
while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
if (d1 != d2)
return d1<d2 ? -1 : 1;
if ((r = memcmp(s1, s2, d1)))
return r;
if (!--length)
return 0;
s1 += d1;
s2 += d1;
if (*s1 == '.') s1++;
if (*s2 == '.') s2++;
}
}
char * partialno(rev1,rev2,length)
struct buf *rev1;
char const *rev2;
register int length;
{
register char *r1;
bufscpy(rev1, rev2);
r1 = rev1->string;
while (length) {
while (*r1!='.' && *r1)
++r1;
++r1;
length--;
}
*(r1-1)='\0';
return rev1->string;
}
static void
store1(store, next)
struct hshentries ***store;
struct hshentry *next;
{
register struct hshentries *p;
p = ftalloc(struct hshentries);
p->first = next;
**store = p;
*store = &p->rest;
}
struct hshentry * genrevs(revno,date,author,state,store)
char const *revno, *date, *author, *state;
struct hshentries **store;
{
int length;
register struct hshentry * next;
int result;
char const *branchnum;
struct buf t;
char datebuf[datesize + zonelenmax];
bufautobegin(&t);
if (!(next = Head)) {
rcserror("RCS file empty");
goto norev;
}
length = countnumflds(revno);
if (length >= 1) {
while ((result=cmpnumfld(revno,next->num,1)) < 0) {
store1(&store, next);
next = next->next;
if (!next) {
rcserror("branch number %s too low", partialno(&t,revno,1));
goto norev;
}
}
if (result>0) {
absent(revno, 1);
goto norev;
}
}
if (length<=1){
branchnum = next->num;
while (next &&
cmpnumfld(branchnum,next->num,1) == 0 &&
(
(date && cmpdate(date,next->date) < 0) ||
(author && strcmp(author,next->author) != 0) ||
(state && strcmp(state,next->state) != 0)
)
)
{
store1(&store, next);
next=next->next;
}
if (!next ||
(cmpnumfld(branchnum,next->num,1)!=0)) {
cantfindbranch(
length ? revno : partialno(&t,branchnum,1),
date, author, state
);
goto norev;
} else {
store1(&store, next);
}
*store = 0;
return next;
}
while ((result=cmpnumfld(revno,next->num,2)) < 0 &&
(cmpnumfld(revno,next->num,1)==0) ) {
store1(&store, next);
next = next->next;
if (!next)
break;
}
if (!next || cmpnumfld(revno,next->num,1) != 0) {
rcserror("revision number %s too low", partialno(&t,revno,2));
goto norev;
}
if ((length>2) && (result!=0)) {
absent(revno, 2);
goto norev;
}
store1(&store, next);
if (length>2)
return genbranch(next,revno,length,date,author,state,store);
else {
if (date && cmpdate(date,next->date)<0) {
rcserror("Revision %s has date %s.",
next->num,
date2str(next->date, datebuf)
);
return 0;
}
if (author && strcmp(author,next->author)!=0) {
rcserror("Revision %s has author %s.",
next->num, next->author
);
return 0;
}
if (state && strcmp(state,next->state)!=0) {
rcserror("Revision %s has state %s.",
next->num,
next->state ? next->state : "<empty>"
);
return 0;
}
*store = 0;
return next;
}
norev:
bufautoend(&t);
return 0;
}
static struct hshentry *
genbranch(bpoint, revno, length, date, author, state, store)
struct hshentry const *bpoint;
char const *revno;
int length;
char const *date, *author, *state;
struct hshentries **store;
{
int field;
register struct hshentry * next, * trail;
register struct branchhead const *bhead;
int result;
struct buf t;
char datebuf[datesize + zonelenmax];
field = 3;
bhead = bpoint->branches;
do {
if (!bhead) {
bufautobegin(&t);
rcserror("no side branches present for %s",
partialno(&t,revno,field-1)
);
bufautoend(&t);
return 0;
}
while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
bhead = bhead->nextbranch;
if (!bhead) {
bufautobegin(&t);
rcserror("branch number %s too high",
partialno(&t,revno,field)
);
bufautoend(&t);
return 0;
}
}
if (result<0) {
absent(revno, field);
return 0;
}
next = bhead->hsh;
if (length==field) {
trail = 0;
do { if ((!date || cmpdate(date,next->date)>=0) &&
(!author || strcmp(author,next->author)==0) &&
(!state || strcmp(state,next->state)==0)
) trail = next;
next=next->next;
} while (next);
if (!trail) {
cantfindbranch(revno, date, author, state);
return 0;
} else {
next = bhead->hsh;
while (next!=trail) {
store1(&store, next);
next=next->next;
}
store1(&store, next);
}
*store = 0;
return next;
}
if (cmpnumfld(revno,next->num,field+1)<0) {
bufautobegin(&t);
rcserror("revision number %s too low",
partialno(&t,revno,field+1)
);
bufautoend(&t);
return 0;
}
do {
store1(&store, next);
trail = next;
next = next->next;
} while (next && cmpnumfld(revno,next->num,field+1)>=0);
if ((length>field+1) &&
(cmpnumfld(revno,trail->num,field+1) !=0)){
absent(revno, field+1);
return 0;
}
if (length == field+1) {
if (date && cmpdate(date,trail->date)<0) {
rcserror("Revision %s has date %s.",
trail->num,
date2str(trail->date, datebuf)
);
return 0;
}
if (author && strcmp(author,trail->author)!=0) {
rcserror("Revision %s has author %s.",
trail->num, trail->author
);
return 0;
}
if (state && strcmp(state,trail->state)!=0) {
rcserror("Revision %s has state %s.",
trail->num,
trail->state ? trail->state : "<empty>"
);
return 0;
}
}
bhead = trail->branches;
} while ((field+=2) <= length);
*store = 0;
return trail;
}
static char const *
lookupsym(id)
char const *id;
{
register struct assoc const *next;
for (next = Symbols; next; next = next->nextassoc)
if (strcmp(id, next->symbol)==0)
return next->num;
return 0;
}
int expandsym(source, target)
char const *source;
struct buf *target;
{
return fexpandsym(source, target, (RILE*)0);
}
int
fexpandsym(source, target, fp)
char const *source;
struct buf *target;
RILE *fp;
{
register char const *sp, *bp;
register char *tp;
char const *tlim;
int dots;
sp = source;
bufalloc(target, 1);
tp = target->string;
if (!sp || !*sp) {
*tp='\0';
return true;
}
if (sp[0] == KDELIM && !sp[1]) {
if (!getoldkeys(fp))
return false;
if (!*prevrev.string) {
workerror("working file lacks revision number");
return false;
}
bufscpy(target, prevrev.string);
return true;
}
tlim = tp + target->size;
dots = 0;
for (;;) {
register char *p = tp;
size_t s = tp - target->string;
int id = false;
for (;;) {
switch (ctab[(unsigned char)*sp]) {
case IDCHAR:
case LETTER:
case Letter:
id = true;
case DIGIT:
if (tlim <= p)
p = bufenlarge(target, &tlim);
*p++ = *sp++;
continue;
default:
break;
}
break;
}
if (tlim <= p)
p = bufenlarge(target, &tlim);
*p = 0;
tp = target->string + s;
if (id) {
bp = lookupsym(tp);
if (!bp) {
rcserror("Symbolic name `%s' is undefined.",tp);
return false;
}
} else {
for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++)
continue;
if (!*bp)
if (s || *sp!='.')
break;
else {
char const *b;
if (Dbranch)
b = Dbranch;
else if (Head)
b = Head->num;
else
break;
getbranchno(b, target);
bp = tp = target->string;
tlim = tp + target->size;
}
}
while ((*tp++ = *bp++))
if (tlim <= tp)
tp = bufenlarge(target, &tlim);
switch (*sp++) {
case '\0':
return true;
case '.':
if (!*sp) {
if (dots & 1)
break;
if (!(bp = branchtip(target->string)))
return false;
bufscpy(target, bp);
return true;
}
++dots;
tp[-1] = '.';
continue;
}
break;
}
rcserror("improper revision number: %s", source);
return false;
}
char const *
namedrev(name, delta)
char const *name;
struct hshentry *delta;
{
if (name) {
char const *id = 0, *p, *val;
for (p = name; ; p++)
switch (ctab[(unsigned char)*p]) {
case IDCHAR:
case LETTER:
case Letter:
id = name;
break;
case DIGIT:
break;
case UNKN:
if (!*p && id &&
(val = lookupsym(id)) &&
strcmp(val, delta->num) == 0
)
return id;
default:
return 0;
}
}
return 0;
}
static char const *
branchtip(branch)
char const *branch;
{
struct hshentry *h;
struct hshentries *hs;
h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs);
return h ? h->num : (char const*)0;
}
char const *
tiprev()
{
return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0;
}
#ifdef REVTEST
char const cmdid[] = "revtest";
int
main(argc,argv)
int argc; char * argv[];
{
static struct buf numricrevno;
char symrevno[100];
char author[20];
char state[20];
char date[20];
struct hshentries *gendeltas;
struct hshentry * target;
int i;
if (argc<2) {
aputs("No input file\n",stderr);
exitmain(EXIT_FAILURE);
}
if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
faterror("can't open input file %s", argv[1]);
}
Lexinit();
getadmin();
gettree();
getdesc(false);
do {
aputs("\nEnter revision number or <return> or '.': ",stderr);
if (!gets(symrevno)) break;
if (*symrevno == '.') break;
aprintf(stderr,"%s;\n",symrevno);
expandsym(symrevno,&numricrevno);
aprintf(stderr,"expanded number: %s; ",numricrevno.string);
aprintf(stderr,"Date: ");
gets(date); aprintf(stderr,"%s; ",date);
aprintf(stderr,"Author: ");
gets(author); aprintf(stderr,"%s; ",author);
aprintf(stderr,"State: ");
gets(state); aprintf(stderr, "%s;\n", state);
target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0,
*state?state:(char*)0, &gendeltas);
if (target) {
while (gendeltas) {
aprintf(stderr,"%s\n",gendeltas->first->num);
gendeltas = gendeltas->next;
}
}
} while (true);
aprintf(stderr,"done\n");
exitmain(EXIT_SUCCESS);
}
void exiterr() { _exit(EXIT_FAILURE); }
#endif