home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
CMDS
/
rcs4.lha
/
ci.c
next >
Wrap
Text File
|
1993-03-03
|
40KB
|
974 lines
/*
* RCS checkin operation
*/
#ifndef lint
static char rcsid[]=
"$Header: /usr/src/local/bin/rcs/src/RCS/ci.c,v 4.6 87/12/18 11:34:41 narten Exp $ Purdue CS";
#endif
/*******************************************************************
* check revisions into RCS files
*******************************************************************
*
* Copyright (C) 1982 by Walter F. Tichy
* Purdue University
* Computer Science Department
* West Lafayette, IN 47907
*
* All rights reserved. No part of this software may be sold or distributed
* in any form or by any means without the prior written permission of the
* author.
* Report problems and direct all inquiries to Tichy@@purdue (ARPA net).
*/
/* $Log: ci.c,v $
* Revision 4.6 87/12/18 11:34:41 narten
* lint cleanups (from Guy Harris)
*
* Revision 4.5 87/10/18 10:18:48 narten
* Updating version numbers. Changes relative to revision 1.1 are actually
* relative to 4.3
*
* Revision 1.3 87/09/24 13:57:19 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:21:33 jenkins
* Port to suns
*
* Revision 1.1 84/01/23 14:49:54 kcs
* Initial revision
*
* Revision 4.3 83/12/15 12:28:54 wft
* ci -u and ci -l now set mode of working file properly.
*
* Revision 4.2 83/12/05 13:40:54 wft
* Merged with 3.9.1.1: added calls to clearerr(stdin).
* made rewriteflag external.
*
* Revision 4.1 83/05/10 17:03:06 wft
* Added option -d and -w, and updated assingment of date, etc. to new delta.
* Added handling of default branches.
* Option -k generates std. log message; fixed undef. pointer in reading of log.
* Replaced getlock() with findlock(), link--unlink with Rename(),
* getpwuid() with getcaller().
* Moved all revision number generation to new routine addelta().
* Removed calls to stat(); now done by pairfilenames().
* Changed most calls to catchints() with restoreints().
* Directed all interactive messages to stderr.
*
* Revision 3.9.1.1 83/10/19 04:21:03 lepreau
* Added clearerr(stdin) to getlogmsg() for re-reading stdin.
*
* Revision 3.9 83/02/15 15:25:44 wft
* 4.2 prerelease
*
* Revision 3.9 83/02/15 15:25:44 wft
* Added call to fastcopy() to copy remainder of RCS file.
*
* Revision 3.8 83/01/14 15:34:05 wft
* Added ignoring of interrupts while new RCS file is renamed;
* Avoids deletion of RCS files by interrupts.
*
* Revision 3.7 82/12/10 16:09:20 wft
* Corrected checking of return code from diff.
*
* Revision 3.6 82/12/08 21:34:49 wft
* Using DATEFORM to prepare date of checked-in revision;
* Fixed return from addbranch().
*
* Revision 3.5 82/12/04 18:32:42 wft
* Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
* field lockedby in removelock(), moved getlogmsg() before calling diff.
*
* Revision 3.4 82/12/02 13:27:13 wft
* added option -k.
*
* Revision 3.3 82/11/28 20:53:31 wft
* Added mustcheckin() to check for redundant checkins.
* Added xpandfile() to do keyword expansion for -u and -l;
* -m appends linefeed to log message if necessary.
* getlogmsg() suppresses prompt if stdin is not a terminal.
* Replaced keeplock with lockflag, fclose() with ffclose(),
* %02d with %.2d, getlogin() with getpwuid().
*
* Revision 3.2 82/10/18 20:57:23 wft
* An RCS file inherits its mode during the first ci from the working file,
* otherwise it stays the same, except that write permission is removed.
* Fixed ci -l, added ci -u (both do an implicit co after the ci).
* Fixed call to getlogin(), added call to getfullRCSname(), added check
* for write error.
* Changed conflicting identifiers.
*
* Revision 3.1 82/10/13 16:04:59 wft
* fixed type of variables receiving from getc() (char -> int).
* added include file dbm.h for getting BYTESIZ. This is used
* to check the return code from diff portably.
*/
#include "rcsbase.h"
#ifndef lint
static char rcsbaseid[] = RCSBASE;
#endif
#include <sys/types.h>
#include <sys/stat.h>
extern int Rename(); /*rename files */
extern char * getcaller(); /*login of caller */
extern struct hshentry * genrevs(); /*generate delta numbers */
extern int nextc; /*next input character */
extern quietflag; /*suppresses diagnostics if true */
extern int nerror; /*counter for errors */
extern char * buildrevision(); /*constructs desired revision */
extern char * checkid(); /*check identifiers */
extern int partime(); /*parse free-format date/time */
extern long maketime(); /*convert parsed time to unix time. */
extern long time(); /*get date and time */
extern struct tm * localtime(); /*convert unixtime into tm-structure */
extern char * getdate(); /*formates current date (forward) */
extern char * mktempfile(); /*temporary file name generator */
extern struct lock * addlock(); /*adds a new lock */
extern char * getlogmsg(); /*obtains log message; forward */
extern struct hshentry * removelock(); /*finds a caller's lock (forward) */
extern struct hshentry * findlock(); /*finds a lock */
extern char * xpandfile(); /*perform keyword expansion; forward */
extern char prevauthor[];
extern char prevdate[];
extern char prevrev[];
extern char prevstate [];
extern FILE * finptr; /* RCS input file */
extern FILE * frewrite; /* new RCS file */
extern int rewriteflag; /* indicates whether input should be */
/* echoed to frewrite */
char * newRCSfilename, * diffilename;
char * RCSfilename,*workfilename,*expfilename,*newworkfilename;
extern struct stat RCSstat, workstat; /* file status of RCS and work file */
extern int haveRCSstat, haveworkstat;/* status indicators */
int copyflag; /* indicates whether a string should be copied into memory*/
char * rev, * state, *msg;
int initflag, rcsinitflag;
int lockflag, keepworkingfile,keepflag;
int forceciflag; /* forces check in */
int symrebindflag; char * symbol;
int textflag; char * textfile;
char * caller; /* caller's login; */
char * author; /* alternate author for -w option */
char altdate[datelength]; /* alternate date for -d */
struct hshentry * targetdelta; /* old delta to be generated */
char * olddeltanum; /* number of old delta */
struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated */
char newdelnum[revlength]; /* holds new revision number */
int newdnumlength; /* actual length of new rev. num. */
char branchpointnum[revlength]; /* number of branchpoint */
struct hshentry newdelta; /* new delta to be inserted */
struct branchhead newbranch; /* new branch to be inserted */
char logmsg[logsize]; /* buffer for log message */
char tmpdir[80];
main (argc, argv)
int argc;
char * argv[];
{
char * nametest;
char * cmdusage; /* holds command format */
char command[NCPPN+50]; /* holds diff commands */
int msglen; /* length of message given by -m */
int exit_stats; /* return code for system() calls */
int newRCSmode; /* mode for RCS file */
long unixtime;
struct tm parseddate, *ftm;
nametest = getenv("TMPDIR");
if (nametest == NULL) {
nametest = "/dd/tmp";
}
strcpy(tmpdir,nametest);
strcat(tmpdir,"/");
catchints();
cmdid = "ci";
cmdusage = "command format:\nci -r[rev] -l[rev] -u[rev] -f[rev] -k[rev] -q[rev] -mmsg -nname -Nname -sstate -t[txtfile] file ...";
rev = state = msg = symbol = textfile = nil;
initflag= rcsinitflag= symrebindflag= textflag= quietflag= false;
forceciflag= lockflag= keepworkingfile= keepflag= false;
caller = getcaller(); author = nil; /* author may be reset by -w */
altdate[0]= '\0'; /* empty alternate date for -d */
while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
switch ((*argv)[1]) {
case 'r':
lockflag=false;
revno: if ((*argv)[2]!='\0') {
if (rev!=nil) warn("Redefinition of revision number");
rev = (*argv)+2;
}
break;
case 'l':
keepworkingfile=lockflag=true;
goto revno;
case 'u':
keepworkingfile=true; lockflag=false;
goto revno;
case 'q':
quietflag=true;
goto revno;
case 'f':
forceciflag=true;
goto revno;
case 'k':
keepflag=true;
goto revno;
case 'm':
if ((*argv)[2]!='\0'){
if (msg!=nil)warn("Redefinition of -m option");
msg = (*argv)+2;
msglen=strlen(msg);
if (msglen >= logsize) {
warn("log message truncated to %d characters",
logsize);
msg[logsize-2]='\n';
msg[logsize-1]='\0';
}
if (msg[msglen-1]!='\n') {
/*append linefeed*/
VOID strcpy(logmsg,msg);msg=logmsg;
msg[msglen] = '\n';
msg[++msglen]= '\0';
}
} else warn("Missing message for -m option");
break;
case 'n':
symrebindflag=false;
if ((*argv)[2]!='\0'){
if (symbol!=nil)warn("Redefinition of symbolic name");
symbol = (*argv)+2;
if (!(nametest=checkid(symbol,' '))||*nametest)
faterror("Name %s must be one word",symbol);
} else warn("Missing name for -n option");
break;
case 'N':
symrebindflag=true;
if ((*argv)[2]!='\0'){
if (symbol!=nil)warn("Redefinition of symbolic name");
symbol = (*argv)+2;
if (!(nametest=checkid(symbol,' '))||*nametest)
faterror("Name %s must be one word",symbol);
} else warn("Missing name for -N option");
break;
case 's':
if ((*argv)[2]!='\0'){
if (state!=nil)warn("Redefinition of -s option");
state = (*argv)+2;
VOID checkid(state,' ');
} else warn("Missing state for -s option");
break;
case 't':
textflag=true;
if ((*argv)[2]!='\0'){
if (textfile!=nil)warn("Redefinition of -t option");
textfile = (*argv)+2;
}
break;
case 'd':
if ((*argv)[2]!='\0'){
if (altdate[0]!='\0')warn("Redefinition of -d option");
/* process the date */
if ( partime((*argv)+2, &parseddate) == 0) {
faterror("Can't parse date/time: %s", (*argv)+2);
break;
}
if ( (unixtime = maketime(&parseddate)) == 0L) {
faterror("Inconsistent date/time: %s",(*argv)+2);
break;
}
ftm = localtime(&unixtime);
VOID sprintf(altdate,DATEFORM,
ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
} else warn("Missing date for -d option");
break;
case 'w':
if ((*argv)[2]!='\0'){
if (author!=nil)warn("Redefinition of -w option");
author = (*argv)+2;
VOID checkid(author,' ');
} else warn("Missing author for -w option");
break;
default:
faterror("unknown option: %s\n%s", *argv,cmdusage);
};
} /* end processing of options */
if (argc<1) faterror("No input file\n%s",cmdusage);
if (!isatty(fileno(stdin)) && msg==nil && textflag && textfile==nil) {
/* would need both log message and descriptive text from a file */
faterror("Can't take both log and description from redirected stdin; use -ttextfile");
}
/* now handle all filenames */
do {
gendeltas[0] = nil;
copyflag=rewriteflag=false;
finptr=frewrite=NULL;
targetdelta=nil;
olddeltanum=nil;
switch (pairfilenames(argc,argv,false,false)) {
case -1: /* New RCS file */
initflag=true; rcsinitflag=false;
break;
case 0: /* Error */
continue;
case 1: /* Normal checkin with prev . RCS file */
initflag=false; rcsinitflag=(Head==nil);
}
/* now RCSfilename contains the name of the RCS file, and
* workfilename contains the name of the working file.
* if !initflag, finptr contains the file descriptor for the
* RCS file. The admin node is initialized.
* workstat and RCSstat are set.
*/
diagnose("%s <-- %s", RCSfilename,workfilename);
if (access(workfilename,1)!=0) {
error("working file %s not readable or nonexistent",
workfilename);
continue;
}
if (!trydiraccess(RCSfilename)) continue; /* give up */
if (!initflag && !checkaccesslist(caller)) continue; /* give up */
if (!trysema(RCSfilename,true)) continue; /* give up */
if (keepflag) {
/* get keyword values from working file */
if (!getoldkeys(workfilename)) continue;
if (rev==nil && *(rev=prevrev)=='\0') {
error("Can't find a revision number in %s",workfilename);
continue;
}
if (*prevdate=='\0' && *altdate=='\0')
warn("Can't find a date in %s",workfilename);
if (*prevauthor=='\0' && author==nil)
warn("Can't find an author in %s", workfilename);
if (*prevstate=='\0' && state==nil)
warn("Can't find a state in %s", workfilename);
} /* end processing keepflag */
gettree(); /* reads in the delta tree.*/
/* expand symbolic revision number */
if (!expandsym(rev,newdelnum)) continue;
/* splice new delta into tree */
if (!addelta()) continue;
if (initflag||rcsinitflag) {
diagnose("initial revision: %s",newdelnum);
} else diagnose("new revision: %s; previous revision: %s",
newdelnum,olddeltanum);
newdelta.num=newdelnum;
newdelta.branches=nil;
newdelta.log=nil;
newdelta.lockedby=nil; /*might be changed by addlock() */
/* set author */
if (author!=nil)
newdelta.author=author; /* set author given by -w */
elsif (keepflag && *prevauthor!='\0')
newdelta.author=prevauthor; /* preserve old author of possible*/
else newdelta.author=caller; /* otherwise use caller's id */
if (state!=nil)
newdelta.state=state; /* set state given by -s */
elsif (keepflag && *prevstate!='\0')
newdelta.state=prevstate; /* preserve old state if possilbe */
else newdelta.state=DEFAULTSTATE;/* otherwise use default state */
if (*altdate!='\0')
newdelta.date=altdate; /* set date given by -d */
elsif (keepflag && *prevdate!='\0') /* preserve old date if possible */
newdelta.date =prevdate;
else
newdelta.date = getdate(); /* use current date */
/* now check validity of date -- needed because of -d and -k */
if (targetdelta!=nil &&
cmpnum(newdelta.date,targetdelta->date)<=0) {
error("Date %s is not later than %s in existing revision %s",
newdelta.date,targetdelta->date, targetdelta->num);
continue;
}
if (lockflag && !addlock(&newdelta,caller)) continue;
if (symbol && !addsymbol(&newdelta,symbol,symrebindflag)) continue;
/* prepare for rewriting the RCS file */
newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
error("Can't open file %s",newRCSfilename);
continue;
}
putadmin(frewrite);
puttree(Head,frewrite);
VOID putdesc(initflag,textflag,textfile,quietflag);
/* build rest of file */
if (initflag||rcsinitflag) {
/* get logmessage */
newdelta.log=getlogmsg();
if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
ffclose(frewrite); frewrite=NULL;
} else {
diffilename=mktempfile(tmpdir,DIFFILE);
if (&newdelta==Head) {
/* prepend new one */
rewriteflag=false;
if (!(expfilename=
buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
if (!mustcheckin(expfilename,targetdelta)) continue;
/* don't check in files that aren't different, unless forced*/
newdelta.log=getlogmsg();
VOID sprintf(command,"%s -n %s %s >-%s\n", DIFF,
workfilename,expfilename,diffilename);
exit_stats = system (command);
if (exit_stats != 0 && exit_stats != 1 )
faterror ("diff failed");
/* diff returns 2 in the upper byte on failure */
if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
if(!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite)) continue;
} else {
/* insert new delta text */
rewriteflag=true;
if (!(expfilename=
buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
if (!mustcheckin(expfilename,targetdelta)) continue;
/* don't check in files that aren't different, unless forced*/
newdelta.log=getlogmsg();
VOID sprintf(command,"%s -n %s %s >-%s\n", DIFF,
expfilename,workfilename,diffilename);
exit_stats = system (command);
if (exit_stats != 0 && exit_stats != 1)
faterror ("diff failed");
if(!putdtext(newdelnum,newdelta.log,diffilename,frewrite)) continue;
}
/* rewrite rest of RCS file */
fastcopy(finptr,frewrite);
ffclose(frewrite); frewrite=NULL;
}
ignoreints();
#ifdef OSK
if ( finptr) {
ffclose( finptr );
finptr = NULL;
}
#endif
if (Rename(newRCSfilename,RCSfilename)<0) {
error("Can't write new RCS file %s; saved in %s",
RCSfilename,newRCSfilename);
newRCSfilename[0]='\0'; /* avoid deletion by cleanup*/
restoreints();
VOID cleanup();
break;
}
newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~022;
/* newRCSmode is also used to adjust mode of working file for -u and -l */
if (chmod(RCSfilename,newRCSmode)<0)
warn("Can't set mode of %s",RCSfilename);
restoreints();
# ifdef SNOOPFILE
logcommand("ci",&newdelta,gendeltas,caller);
# endif
if (!keepworkingfile) {
VOID unlink(workfilename); /* get rid of old file */
} else {
/* expand keywords in file */
newworkfilename=
xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
if (!newworkfilename) continue; /* expand failed */
ignoreints();
if (Rename(newworkfilename,workfilename) <0) {
error("Can't expand keywords in %s",workfilename);
restoreints();
continue;
}
newworkfilename[0]='\0'; /* avoid re-unlink by cleanup */
if (chmod(workfilename, WORKMODE(newRCSmode))<0)
warn("Can't adjust mode of %s",workfilename);
restoreints();
}
diagnose("done");
} while (cleanup(),
++argv, --argc >=1);
exit(nerror!=0);
/*NOTREACHED*/
} /* end of main (ci) */
/*****************************************************************/
/* the rest are auxiliary routines */
int addelta()
/* Function: Appends a delta to the delta tree, whose number is
* given by newdelnum[]. Updates Head, newdelnum, newdenumlength,
* olddeltanum and the links in newdelta.
* Retruns false on error, true on success.
*/
{
register char * sp, * tp;
register int i;
newdnumlength=countnumflds(newdelnum);
if (initflag || rcsinitflag ) {
/* this covers non-existing RCS file and a file initialized with rcs -i */
if ((newdnumlength==0)&&(Dbranch!=nil)) {
VOID strcpy(newdelnum,Dbranch->num);
newdnumlength=countnumflds(newdelnum);
}
if (newdnumlength==0) VOID strcpy(newdelnum,"1.1");
elsif (newdnumlength==1) VOID strcat(newdelnum,".1");
elsif (newdnumlength>2) {
error("Branch point does not exist for %s",newdelnum);
return false;
} /* newdnumlength == 2 is OK; */
olddeltanum=nil;
Head = &newdelta;
newdelta.next=nil;
return true;
}
if (newdnumlength==0) {
/* derive new revision number from locks */
targetdelta=findlock(caller,true); /*find and delete it*/
if (targetdelta) {
/* found an old lock */
olddeltanum=targetdelta->num;
/* check whether locked revision exists */
if (!genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas)) return false;
if (targetdelta==Head) {
/* make new head */
newdelta.next=Head;
Head= &newdelta;
incnum(olddeltanum, newdelnum);
} elsif ((targetdelta->next==nil)&&(countnumflds(olddeltanum)>2)) {
/* new tip revision on side branch */
targetdelta->next= &newdelta;
newdelta.next = nil;
incnum(olddeltanum, newdelnum);
} else {
/* middle revision; start a new branch */
newdelnum[0]='\0';
if (!addbranch(targetdelta,newdelnum)) return false;
}
return true; /* successfull use of existing lock */
} else {
/* no existing lock; try Dbranch */
/* update newdelnum */
if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
error("no lock set by %s",caller);
return false;
}
if (Dbranch) {
VOID strcpy(newdelnum,Dbranch->num);
} else {
incnum(Head->num,newdelnum);
}
newdnumlength=countnumflds(newdelnum);
/* now fall into next statement */
}
}
if (newdnumlength<=2) {
/* add new head per given number */
olddeltanum=Head->num;
if(newdnumlength==1) {
/* make a two-field number out of it*/
if (cmpnumfld(newdelnum,olddeltanum,1)==0)
incnum(olddeltanum,newdelnum);
else VOID strcat(newdelnum, ".1");
}
if (cmpnum(newdelnum,olddeltanum) <= 0) {
error("deltanumber %s too low; must be higher than %s",
newdelnum,Head->num);
return false;
}
if (!(targetdelta=removelock(caller,Head))) return false;
if (!(genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas))) return false;
newdelta.next=Head;
Head= &newdelta;
} else {
/* put new revision on side branch */
/*first, get branch point */
tp=branchpointnum; sp=newdelnum;
for(i=newdnumlength-(newdnumlength%2==1?1:2);i>0;i--) {
while (*sp != '.') *tp++ = *sp++; /*copy field*/
*tp++ = *sp++; /*copy dot */
}
*(tp-1) = '\0'; /* kill final dot */
olddeltanum=branchpointnum; /*temporary old delta*/
if (!(targetdelta=genrevs(branchpointnum,(char *)nil,(char *)nil,(char *)nil,gendeltas)))
return false;
if (cmpnum(targetdelta->num,branchpointnum)!=0) {
error("Cannot find branchpoint %s",branchpointnum);
return false;
}
if (!addbranch(targetdelta,newdelnum)) return false;
}
return true;
}
int addbranch(branchpoint,num)
struct hshentry * branchpoint;
char * num;
/* adds a new branch and branch delta at branchpoint.
* If num is the null string, appends the new branch, incrementing
* the highest branch number (initially 1), and setting the level number to 1.
* the new delta and branchhead are in globals newdelta and newbranch, resp.
* the new number is placed into num.
* returns false on error.
*/
{
struct branchhead * bhead, * btrail;
char branchnum[revlength];
int numlength, result, field;
numlength = countnumflds(num);
if (branchpoint->branches==nil) {
/* start first branch */
branchpoint->branches = &newbranch;
if (numlength==0) {
VOID strcpy(num, branchpoint->num);
VOID strcat(num,".1.1");
} elsif(countnumflds(num)%2 == 1)
VOID strcat(num, ".1");
newbranch.nextbranch=nil;
} elsif (numlength==0) {
/* append new branch to the end */
bhead=branchpoint->branches;
while (bhead->nextbranch) bhead=bhead->nextbranch;
bhead->nextbranch = &newbranch;
getbranchno(bhead->hsh->num,branchnum);
incnum(branchnum,num);
VOID strcat(num,".1");
newbranch.nextbranch=nil;
} else {
/* place the branch properly */
field = numlength - (numlength%2 ==1?0:1);
/* field of branch number */
bhead=branchpoint->branches;
while ((bhead!=nil) &&
((result=cmpnumfld(num,bhead->hsh->num,field))>0)) {
btrail=bhead;
bhead=bhead->nextbranch;
}
if (bhead==nil || result<0) {
/* insert/append new branchhead */
if (bhead==branchpoint->branches)
branchpoint->branches= &newbranch;
else btrail->nextbranch= &newbranch;
newbranch.nextbranch=bhead;
if (numlength%2 ==1) VOID strcat(num,".1");
} else {
/* branch exists; append to end */
getbranchno(num,branchnum);
if (!(targetdelta=genrevs(branchnum,(char *)nil,(char *)nil,(char *)nil,
gendeltas))) return false;
olddeltanum=targetdelta->num;
if (cmpnum(num,olddeltanum) <= 0) {
error("deltanumber %s too low; must be higher than %s",
num,olddeltanum);
return false;
}
if (!removelock(caller,targetdelta)) return false;
if (numlength%2==1) incnum(olddeltanum,num);
targetdelta->next= &newdelta;
newdelta.next=nil;
return true; /* Don't do anything to newbranch */
}
}
newbranch.hsh = &newdelta;
newdelta.next=nil;
return true;
}
struct hshentry * removelock(who,delta)
char * who; struct hshentry * delta;
/* function: Finds the lock held by who on delta,
* removes it, and returns a pointer to the delta.
* Prints an error message and returns nil if there is no such lock.
* An exception is if StrictLocks==false, and who is the owner of
* the RCS file. If who does not have a lock in this case,
* delta is returned.
*/
{
register struct lock * next, * trail;
char * num;
struct lock dummy;
int whomatch, nummatch;
num=delta->num;
dummy.nextlock=next=Locks;
trail = &dummy;
while (next!=nil) {
whomatch=strcmp(who,next->login);
nummatch=strcmp(num,next->delta->num);
if ((whomatch==0) && (nummatch==0)) break;
/*found a lock on delta by who*/
if ((whomatch!=0)&&(nummatch==0)) {
error("revision %s locked by %s",num,next->login);
return nil;
}
trail=next;
next=next->nextlock;
}
if (next!=nil) {
/*found one; delete it */
trail->nextlock=next->nextlock;
Locks=dummy.nextlock;
next->delta->lockedby=nil; /* reset locked-by */
return next->delta;
} else {
if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
error("no lock set by %s for revision %s",who,num);
return nil;
} else {
return delta;
}
}
}
char * getdate()
/* Function: returns a pointer to the current date in the form
* YY.MM.DD.hh.mm.ss\0
*/
{
long clock;
struct tm * tm;
static char buffer[datelength]; /* date buffer */
clock=time((long *)0);
tm=localtime(&clock);
VOID sprintf(buffer, DATEFORM,
tm->tm_year, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return buffer;
}
char * xpandfile (unexfname,dir,delta)
char * unexfname, * dir;
struct hshentry * delta;
/* Function: Reads file unexpfname and copies it to a
* file in dir, performing keyword substitution with data from delta.
* returns the name of the expanded file if successful, nil otherwise.
*/
{ char * targetfname;
FILE * unexfile, *exfile;
targetfname=mktempfile(dir,TMPFILE3);
if ((unexfile=fopen(unexfname, "r" ))==NULL ||
(exfile =fopen(targetfname,"w"))==NULL) {
error("Can't expand file %s",unexfname);
return nil;
}
while (expandline(unexfile,exfile,delta,false,false)); /*expand*/
ffclose(unexfile);ffclose(exfile);
return targetfname;
}
mustcheckin (unexfname,delta)
char * unexfname; struct hshentry * delta;
/* Function: determines whether checkin should proceed.
* Compares the wrkfilename with unexfname, disregarding keywords.
* If the 2 files differ, returns true. If they do not differ, asks the user
* whether to return true or false (i.e., whether to checkin the file anyway.
* If the files do not differ, and quietflag==true, returns false.
* Shortcut: If forceciflag==true, mustcheckin() always returns true.
*/
{ register int c;
int response, result;
if (forceciflag) return true;
if (!rcsfcmp(workfilename,unexfname,delta)) return true;
/* If files are different, must check them in. */
/* files are the same */
diagnose("File %s is unchanged with respect to revision %s",
workfilename,delta->num);
if (quietflag || !isatty(fileno(stdin))) {
/* Files are the same, but can't ask, so don't checkin*/
result=false;
} else {
/* ask user whether to check in */
VOID fputs("checkin anyway? [ny](n): ",stderr);
fflush(stderr); response=c=getchar();
while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
result=(response=='y'||response=='Y');
}
if (result==false) {
if (quietflag) {
warn("checkin aborted since %s was not changed; %s %sdeleted.",
workfilename,workfilename,keepworkingfile?"not ":"");
} else {
diagnose("checkin aborted; %s %sdeleted.",
workfilename,keepworkingfile?"not ":"");
}
if (!keepworkingfile) VOID unlink(workfilename);
}
return result;
}
/* --------------------- G E T L O G M S G --------------------------------*/
extern int stdinread; /* is >0 if redirected stdin has been read once. */
char * getlogmsg()
/* Function: obtains a log message and returns a pointer to it.
* If a log message is given via the -m option, a pointer to that
* string is returned.
* If this is the initial revision, a standard log message is returned.
* Otherwise, reads a character string from the terminal.
* The string must be terminated with a control-d or a single '.' on a
* line. getlogmsg prompts the first time it is called for the
* log message; during all later calls it asks whether the previous
* log message can be reused.
* returns a pointer to the character string; the pointer is always non-nil.
*/
{
static logyet = false; /*indicates whether previous log present*/
static char emptylog[] = "*** empty log message ***\n";
static char initiallog[]= "Initial revision\n";
char response;
int cin;
register char c, old1, old2, * tp;
if (msg) return msg;
if ((olddeltanum==nil)&&
((cmpnum(newdelnum,"1.1")==0)||(cmpnum(newdelnum,"1.0")==0))) {
return initiallog;
}
if (keepflag) {
/* generate std. log message */
VOID sprintf(logmsg, "checked in with -k by %s at %s.\n",caller,getdate());
return(logmsg);
}
if (logyet) {
/*previous log available*/
if (!isatty(fileno(stdin))) return logmsg; /* reuse if stdin is not a terminal*/
/* otherwise ask */
cleareof(stdin);clearerr(stdin); /* reset EOF ptr */
VOID fputs("reuse log message of previous file? [yn](y): ",stderr);
fflush(stderr);cin=getchar();
response=cin;
while (!(cin==EOF || cin=='\n')) cin=getchar();/*skip to end of line*/
if (response=='\n'||response=='y'||response=='Y')
return logmsg;
else
logmsg[0]='\0'; /*kill existing log message */
}
/* now read string from stdin */
if (isatty(fileno(stdin))) {
VOID fputs("enter log message:\n(terminate with <eof> or single '.')\n>> ",stderr);
fflush(stderr);
} else { /* redirected stdin */
if (stdinread>0)
faterror("Can't reread redirected stdin for log message; use -m");
stdinread++;
}
tp=logmsg; old1='\n'; old2=' ';
if (feof(stdin))
cleareof(stdin),clearerr(stdin);
for (;;) {
cin=getchar();
if (cin==EOF) {
if(isatty(fileno(stdin))) VOID putc('\n',stderr);
if ((tp==logmsg)||(*(tp-1)!='\n')) *tp++ = '\n'; /* append newline */
*tp = '\0'; /*terminate*/
break;
}
if (cin=='\n' && old1=='.' && old2=='\n') {
*(tp-1) = '\0'; /*kill last period */
break;
}
if (tp>=logmsg+logsize-2) { /* overflow */
if (!isatty(fileno(stdin))) {
warn("log message truncated to %d characters",logsize);
logmsg[logsize-2]='\n';logmsg[logsize-1]='\0';
return logmsg;
}
VOID fprintf(stderr,"log message too long. Maximum: %d\n",logsize);
VOID fputs("reenter log message:\n>> ",stderr);
fflush(stderr);
tp=logmsg; old1='\n'; old2=' ';
while (cin!='\n') cin=getchar(); /*skip line */
continue;
}
if (cin=='\n' && isatty(fileno(stdin))) {
VOID fputs(">> ",stderr);
fflush(stderr);
}
*tp++ = cin; old2=old1; old1=cin; /* this is the actual work!*/
/*SDELIM will be changed to double SDELIM by putdtext*/
} /* end for */
/* now check whether the log message is not empty */
tp=logmsg;
while ((c= *tp++)==' '||c=='\t'||c=='\n'||c=='\f');
if (*tp=='\0') {
logyet=false;
return emptylog;
} else {
logyet=true;
return logmsg;
}
}