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 >
Text File  |  1993-03-03  |  40KB  |  974 lines

  1. /*
  2.  *                     RCS checkin operation
  3.  */
  4. #ifndef lint
  5.  static char rcsid[]=
  6.  "$Header: /usr/src/local/bin/rcs/src/RCS/ci.c,v 4.6 87/12/18 11:34:41 narten Exp $ Purdue CS";
  7. #endif
  8. /*******************************************************************
  9.  *                       check revisions into RCS files
  10.  *******************************************************************
  11.  *
  12.  * Copyright (C) 1982 by Walter F. Tichy
  13.  *                       Purdue University
  14.  *                       Computer Science Department
  15.  *                       West Lafayette, IN 47907
  16.  *
  17.  * All rights reserved. No part of this software may be sold or distributed
  18.  * in any form or by any means without the prior written permission of the
  19.  * author.
  20.  * Report problems and direct all inquiries to Tichy@@purdue (ARPA net).
  21.  */
  22.  
  23.  
  24.  
  25. /* $Log:    ci.c,v $
  26.  * Revision 4.6  87/12/18  11:34:41  narten
  27.  * lint cleanups (from Guy Harris)
  28.  * 
  29.  * Revision 4.5  87/10/18  10:18:48  narten
  30.  * Updating version numbers. Changes relative to revision 1.1 are actually
  31.  * relative to 4.3
  32.  * 
  33.  * Revision 1.3  87/09/24  13:57:19  narten
  34.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  35.  * warnings)
  36.  * 
  37.  * Revision 1.2  87/03/27  14:21:33  jenkins
  38.  * Port to suns
  39.  * 
  40.  * Revision 1.1  84/01/23  14:49:54  kcs
  41.  * Initial revision
  42.  * 
  43.  * Revision 4.3  83/12/15  12:28:54  wft
  44.  * ci -u and ci -l now set mode of working file properly.
  45.  * 
  46.  * Revision 4.2  83/12/05  13:40:54  wft
  47.  * Merged with 3.9.1.1: added calls to clearerr(stdin).
  48.  * made rewriteflag external.
  49.  * 
  50.  * Revision 4.1  83/05/10  17:03:06  wft
  51.  * Added option -d and -w, and updated assingment of date, etc. to new delta.
  52.  * Added handling of default branches.
  53.  * Option -k generates std. log message; fixed undef. pointer in reading of log.
  54.  * Replaced getlock() with findlock(), link--unlink with Rename(),
  55.  * getpwuid() with getcaller().
  56.  * Moved all revision number generation to new routine addelta().
  57.  * Removed calls to stat(); now done by pairfilenames().
  58.  * Changed most calls to catchints() with restoreints().
  59.  * Directed all interactive messages to stderr.
  60.  * 
  61.  * Revision 3.9.1.1  83/10/19  04:21:03  lepreau
  62.  * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
  63.  * 
  64.  * Revision 3.9  83/02/15  15:25:44  wft
  65.  * 4.2 prerelease
  66.  * 
  67.  * Revision 3.9  83/02/15  15:25:44  wft
  68.  * Added call to fastcopy() to copy remainder of RCS file.
  69.  *
  70.  * Revision 3.8  83/01/14  15:34:05  wft
  71.  * Added ignoring of interrupts while new RCS file is renamed;
  72.  * Avoids deletion of RCS files by interrupts.
  73.  *
  74.  * Revision 3.7  82/12/10  16:09:20  wft
  75.  * Corrected checking of return code from diff.
  76.  *
  77.  * Revision 3.6  82/12/08  21:34:49  wft
  78.  * Using DATEFORM to prepare date of checked-in revision;
  79.  * Fixed return from addbranch().
  80.  *
  81.  * Revision 3.5  82/12/04  18:32:42  wft
  82.  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
  83.  * field lockedby in removelock(), moved getlogmsg() before calling diff.
  84.  *
  85.  * Revision 3.4  82/12/02  13:27:13  wft
  86.  * added option -k.
  87.  *
  88.  * Revision 3.3  82/11/28  20:53:31  wft
  89.  * Added mustcheckin() to check for redundant checkins.
  90.  * Added xpandfile() to do keyword expansion for -u and -l;
  91.  * -m appends linefeed to log message if necessary.
  92.  * getlogmsg() suppresses prompt if stdin is not a terminal.
  93.  * Replaced keeplock with lockflag, fclose() with ffclose(),
  94.  * %02d with %.2d, getlogin() with getpwuid().
  95.  *
  96.  * Revision 3.2  82/10/18  20:57:23  wft
  97.  * An RCS file inherits its mode during the first ci from the working file,
  98.  * otherwise it stays the same, except that write permission is removed.
  99.  * Fixed ci -l, added ci -u (both do an implicit co after the ci).
  100.  * Fixed call to getlogin(), added call to getfullRCSname(), added check
  101.  * for write error.
  102.  * Changed conflicting identifiers.
  103.  *
  104.  * Revision 3.1  82/10/13  16:04:59  wft
  105.  * fixed type of variables receiving from getc() (char -> int).
  106.  * added include file dbm.h for getting BYTESIZ. This is used
  107.  * to check the return code from diff portably.
  108.  */
  109.  
  110. #include "rcsbase.h"
  111. #ifndef lint
  112. static char rcsbaseid[] = RCSBASE;
  113. #endif
  114. #include <sys/types.h>
  115. #include <sys/stat.h>
  116.  
  117. extern int    Rename();                /*rename files                       */
  118. extern char * getcaller();             /*login of caller                    */
  119. extern struct hshentry * genrevs();    /*generate delta numbers             */
  120. extern int  nextc;                     /*next input character               */
  121. extern quietflag;                      /*suppresses diagnostics if true     */
  122. extern int  nerror;                    /*counter for errors                 */
  123. extern char * buildrevision();         /*constructs desired revision        */
  124. extern char * checkid();               /*check identifiers                  */
  125. extern int    partime();               /*parse free-format date/time        */
  126. extern long   maketime();              /*convert parsed time to unix time.  */
  127. extern long   time();                  /*get date and time                  */
  128. extern struct tm * localtime();        /*convert unixtime into tm-structure */
  129. extern char * getdate();               /*formates current date  (forward)   */
  130. extern char * mktempfile();            /*temporary file name generator      */
  131. extern struct lock * addlock();        /*adds a new lock                    */
  132. extern char * getlogmsg();             /*obtains log message; forward       */
  133. extern struct hshentry * removelock(); /*finds a caller's lock  (forward)   */
  134. extern struct hshentry * findlock();   /*finds a lock                       */
  135. extern char * xpandfile();             /*perform keyword expansion; forward */
  136.  
  137. extern char prevauthor[];
  138. extern char prevdate[];
  139. extern char prevrev[];
  140. extern char prevstate [];
  141. extern FILE * finptr;                  /* RCS input file                    */
  142. extern FILE * frewrite;                /* new RCS file                      */
  143. extern int    rewriteflag;             /* indicates whether input should be */
  144.                        /* echoed to frewrite                */
  145.  
  146. char * newRCSfilename, * diffilename;
  147. char * RCSfilename,*workfilename,*expfilename,*newworkfilename;
  148. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  149. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  150.  
  151.  
  152. int    copyflag;    /* indicates whether a string should be copied into memory*/
  153.  
  154. char * rev, * state, *msg;
  155.  
  156. int initflag, rcsinitflag;
  157. int lockflag, keepworkingfile,keepflag;
  158. int forceciflag;                      /* forces check in                    */
  159. int symrebindflag; char * symbol;
  160. int textflag; char * textfile;
  161. char * caller;                        /* caller's login;                    */
  162. char * author;                        /* alternate author for -w option     */
  163. char altdate[datelength];             /* alternate date for -d              */
  164. struct hshentry * targetdelta;        /* old delta to be generated          */
  165. char   * olddeltanum;                 /* number of old delta                */
  166. struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated      */
  167. char   newdelnum[revlength];          /* holds new revision number          */
  168. int    newdnumlength;                 /* actual length of new rev. num.     */
  169. char   branchpointnum[revlength];     /* number of branchpoint              */
  170. struct hshentry newdelta;             /* new delta to be inserted           */
  171. struct branchhead newbranch;          /* new branch to be inserted          */
  172. char   logmsg[logsize];               /* buffer for log message             */
  173.  
  174. char    tmpdir[80];
  175.  
  176. main (argc, argv)
  177. int argc;
  178. char * argv[];
  179. {
  180.     char * nametest;
  181.         char * cmdusage;         /* holds command format                    */
  182.         char command[NCPPN+50];  /* holds diff commands                     */
  183.         int  msglen;             /* length of message given by -m           */
  184.         int exit_stats;          /* return code for system() calls          */
  185.     int newRCSmode;          /* mode for RCS file                       */
  186.     long unixtime;
  187.     struct tm  parseddate, *ftm;
  188.  
  189.     nametest = getenv("TMPDIR");
  190.     if (nametest == NULL) {
  191.         nametest = "/dd/tmp";
  192.     }
  193.     strcpy(tmpdir,nametest);
  194.     strcat(tmpdir,"/");
  195.  
  196.     catchints();
  197.         cmdid = "ci";
  198.         cmdusage = "command format:\nci -r[rev] -l[rev] -u[rev] -f[rev] -k[rev] -q[rev] -mmsg -nname -Nname -sstate -t[txtfile] file ...";
  199.     rev = state = msg = symbol = textfile = nil;
  200.         initflag= rcsinitflag= symrebindflag= textflag= quietflag= false;
  201.         forceciflag= lockflag= keepworkingfile= keepflag= false;
  202.     caller = getcaller(); author = nil; /* author may be reset by -w */
  203.     altdate[0]= '\0'; /* empty alternate date for -d */
  204.  
  205.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  206.                 switch ((*argv)[1]) {
  207.  
  208.                 case 'r':
  209.                         lockflag=false;
  210.                 revno:  if ((*argv)[2]!='\0') {
  211.                                 if (rev!=nil) warn("Redefinition of revision number");
  212.                                 rev = (*argv)+2;
  213.                         }
  214.                         break;
  215.  
  216.                 case 'l':
  217.                         keepworkingfile=lockflag=true;
  218.                         goto revno;
  219.  
  220.                 case 'u':
  221.                         keepworkingfile=true; lockflag=false;
  222.                         goto revno;
  223.  
  224.                 case 'q':
  225.                         quietflag=true;
  226.                         goto revno;
  227.  
  228.                 case 'f':
  229.                         forceciflag=true;
  230.                         goto revno;
  231.  
  232.                 case 'k':
  233.                         keepflag=true;
  234.                         goto revno;
  235.  
  236.                 case 'm':
  237.                         if ((*argv)[2]!='\0'){
  238.                                 if (msg!=nil)warn("Redefinition of -m option");
  239.                                 msg = (*argv)+2;
  240.                                 msglen=strlen(msg);
  241.                                 if (msglen >= logsize) {
  242.                                    warn("log message truncated to %d characters",
  243.                                         logsize);
  244.                                    msg[logsize-2]='\n';
  245.                                    msg[logsize-1]='\0';
  246.                                 }
  247.                                 if (msg[msglen-1]!='\n') {
  248.                                    /*append linefeed*/
  249.                                    VOID strcpy(logmsg,msg);msg=logmsg;
  250.                                    msg[msglen]  = '\n';
  251.                                    msg[++msglen]= '\0';
  252.                                 }
  253.                         } else warn("Missing message for -m option");
  254.                         break;
  255.  
  256.                 case 'n':
  257.                         symrebindflag=false;
  258.                         if ((*argv)[2]!='\0'){
  259.                                 if (symbol!=nil)warn("Redefinition of symbolic name");
  260.                                 symbol = (*argv)+2;
  261.                 if (!(nametest=checkid(symbol,' '))||*nametest)
  262.                     faterror("Name %s must be one word",symbol);
  263.                         } else warn("Missing name for -n option");
  264.                         break;
  265.  
  266.                 case 'N':
  267.                         symrebindflag=true;
  268.                         if ((*argv)[2]!='\0'){
  269.                                 if (symbol!=nil)warn("Redefinition of symbolic name");
  270.                                 symbol = (*argv)+2;
  271.                 if (!(nametest=checkid(symbol,' '))||*nametest)
  272.                     faterror("Name %s must be one word",symbol);
  273.                         } else warn("Missing name for -N option");
  274.                         break;
  275.  
  276.                 case 's':
  277.                         if ((*argv)[2]!='\0'){
  278.                                 if (state!=nil)warn("Redefinition of -s option");
  279.                                 state = (*argv)+2;
  280.                                 VOID checkid(state,' ');
  281.                         } else warn("Missing state for -s option");
  282.                         break;
  283.  
  284.                 case 't':
  285.                         textflag=true;
  286.                         if ((*argv)[2]!='\0'){
  287.                                 if (textfile!=nil)warn("Redefinition of -t option");
  288.                                 textfile = (*argv)+2;
  289.                         }
  290.                         break;
  291.  
  292.         case 'd':
  293.                         if ((*argv)[2]!='\0'){
  294.                 if (altdate[0]!='\0')warn("Redefinition of -d option");
  295.                 /* process the date */
  296.                 if ( partime((*argv)+2, &parseddate) == 0) {
  297.                     faterror("Can't parse date/time: %s", (*argv)+2);
  298.                     break;
  299.                 }
  300.                 if ( (unixtime = maketime(&parseddate)) == 0L) {
  301.                     faterror("Inconsistent date/time: %s",(*argv)+2);
  302.                     break;
  303.                 }
  304.                 ftm = localtime(&unixtime);
  305.                 VOID sprintf(altdate,DATEFORM,
  306.                 ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  307.             } else  warn("Missing date for -d option");
  308.                         break;
  309.  
  310.         case 'w':
  311.                         if ((*argv)[2]!='\0'){
  312.                 if (author!=nil)warn("Redefinition of -w option");
  313.                 author = (*argv)+2;
  314.                 VOID checkid(author,' ');
  315.             } else warn("Missing author for -w option");
  316.                         break;
  317.  
  318.  
  319.  
  320.  
  321.                 default:
  322.                         faterror("unknown option: %s\n%s", *argv,cmdusage);
  323.                 };
  324.         }  /* end processing of options */
  325.  
  326.         if (argc<1) faterror("No input file\n%s",cmdusage);
  327.  
  328.         if (!isatty(fileno(stdin)) && msg==nil && textflag && textfile==nil) {
  329.                 /* would need both log message and descriptive text from a file */
  330.                 faterror("Can't take both log and description from redirected stdin; use -ttextfile");
  331.         }
  332.         /* now handle all filenames */
  333.         do {
  334.         gendeltas[0] = nil;
  335.         copyflag=rewriteflag=false;
  336.         finptr=frewrite=NULL;
  337.         targetdelta=nil;
  338.         olddeltanum=nil;
  339.  
  340.         switch (pairfilenames(argc,argv,false,false)) {
  341.  
  342.         case -1:                /* New RCS file */
  343.                 initflag=true; rcsinitflag=false;
  344.                 break;
  345.  
  346.         case 0:                 /* Error */
  347.                 continue;
  348.  
  349.         case 1:                 /* Normal checkin with prev . RCS file */
  350.                 initflag=false; rcsinitflag=(Head==nil);
  351.         }
  352.  
  353.         /* now RCSfilename contains the name of the RCS file, and
  354.          * workfilename contains the name of the working file.
  355.          * if !initflag, finptr contains the file descriptor for the
  356.          * RCS file. The admin node is initialized.
  357.          * workstat and RCSstat are set.
  358.          */
  359.  
  360.         diagnose("%s  <--  %s", RCSfilename,workfilename);
  361.  
  362.         if (access(workfilename,1)!=0) {
  363.                 error("working file %s not readable or nonexistent",
  364.                        workfilename);
  365.                 continue;
  366.         }
  367.  
  368.         if (!trydiraccess(RCSfilename)) continue; /* give up */
  369.         if (!initflag && !checkaccesslist(caller))   continue; /* give up */
  370.         if (!trysema(RCSfilename,true)) continue; /* give up */
  371.  
  372.         if (keepflag) {
  373.                 /* get keyword values from working file */
  374.                 if (!getoldkeys(workfilename)) continue;
  375.                 if (rev==nil && *(rev=prevrev)=='\0') {
  376.                         error("Can't find a revision number in %s",workfilename);
  377.                         continue;
  378.                 }
  379.         if (*prevdate=='\0' && *altdate=='\0')
  380.             warn("Can't find a date in %s",workfilename);
  381.         if (*prevauthor=='\0' && author==nil)
  382.                         warn("Can't find an author in %s", workfilename);
  383.         if (*prevstate=='\0' && state==nil)
  384.                         warn("Can't find a state in %s", workfilename);
  385.         } /* end processing keepflag */
  386.  
  387.         gettree(); /* reads in the delta tree.*/
  388.  
  389.         /* expand symbolic revision number */
  390.         if (!expandsym(rev,newdelnum)) continue;
  391.  
  392.         /* splice new delta into tree */
  393.         if (!addelta()) continue;
  394.  
  395.         if (initflag||rcsinitflag) {
  396.                 diagnose("initial revision: %s",newdelnum);
  397.         } else  diagnose("new revision: %s; previous revision: %s",
  398.                 newdelnum,olddeltanum);
  399.  
  400.         newdelta.num=newdelnum;
  401.         newdelta.branches=nil;
  402.         newdelta.log=nil;
  403.         newdelta.lockedby=nil; /*might be changed by addlock() */
  404.     /* set author */
  405.     if (author!=nil)
  406.         newdelta.author=author;     /* set author given by -w         */
  407.     elsif (keepflag && *prevauthor!='\0')
  408.         newdelta.author=prevauthor; /* preserve old author of possible*/
  409.     else    newdelta.author=caller;     /* otherwise use caller's id      */
  410.     if (state!=nil)
  411.         newdelta.state=state;       /* set state given by -s          */
  412.     elsif (keepflag && *prevstate!='\0')
  413.         newdelta.state=prevstate;   /* preserve old state if possilbe */
  414.     else    newdelta.state=DEFAULTSTATE;/* otherwise use default state    */
  415.     if (*altdate!='\0')
  416.         newdelta.date=altdate;      /* set date given by -d           */
  417.     elsif (keepflag && *prevdate!='\0') /* preserve old date if possible  */
  418.                 newdelta.date  =prevdate;
  419.     else
  420.         newdelta.date = getdate();  /* use current date               */
  421.     /* now check validity of date -- needed because of -d and -k          */
  422.     if (targetdelta!=nil &&
  423.         cmpnum(newdelta.date,targetdelta->date)<=0) {
  424.         error("Date %s is not later than %s in existing revision %s",
  425.                newdelta.date,targetdelta->date, targetdelta->num);
  426.         continue;
  427.     }
  428.  
  429.  
  430.         if (lockflag && !addlock(&newdelta,caller)) continue;
  431.         if (symbol && !addsymbol(&newdelta,symbol,symrebindflag)) continue;
  432.  
  433.         /* prepare for rewriting the RCS file */
  434.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  435.         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  436.                 error("Can't open file %s",newRCSfilename);
  437.                 continue;
  438.         }
  439.         putadmin(frewrite);
  440.         puttree(Head,frewrite);
  441.         VOID putdesc(initflag,textflag,textfile,quietflag);
  442.  
  443.  
  444.         /* build rest of file */
  445.         if (initflag||rcsinitflag) {
  446.                 /* get logmessage */
  447.                 newdelta.log=getlogmsg();
  448.                 if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
  449.                 ffclose(frewrite); frewrite=NULL;
  450.         } else {
  451.                 diffilename=mktempfile(tmpdir,DIFFILE);
  452.                 if (&newdelta==Head) {
  453.                         /* prepend new one */
  454.                         rewriteflag=false;
  455.                         if (!(expfilename=
  456.                               buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
  457.                         if (!mustcheckin(expfilename,targetdelta)) continue;
  458.                                 /* don't check in files that aren't different, unless forced*/
  459.                         newdelta.log=getlogmsg();
  460.                         VOID sprintf(command,"%s -n %s %s >-%s\n", DIFF,
  461.                                 workfilename,expfilename,diffilename);
  462.                         exit_stats = system (command);
  463.                         if (exit_stats != 0 && exit_stats != 1 )
  464.                             faterror ("diff failed");
  465.                         /* diff returns 2 in the upper byte on failure */
  466.                         if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
  467.                         if(!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite)) continue;
  468.                 } else {
  469.                         /* insert new delta text */
  470.                         rewriteflag=true;
  471.                         if (!(expfilename=
  472.                               buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
  473.                         if (!mustcheckin(expfilename,targetdelta)) continue;
  474.                                 /* don't check in files that aren't different, unless forced*/
  475.                         newdelta.log=getlogmsg();
  476.                         VOID sprintf(command,"%s -n %s %s >-%s\n", DIFF,
  477.                                 expfilename,workfilename,diffilename);
  478.                         exit_stats = system (command);
  479.                         if (exit_stats != 0 && exit_stats != 1)
  480.                             faterror ("diff failed");
  481.                         if(!putdtext(newdelnum,newdelta.log,diffilename,frewrite)) continue;
  482.                 }
  483.  
  484.                 /* rewrite rest of RCS file */
  485.                 fastcopy(finptr,frewrite);
  486.                 ffclose(frewrite); frewrite=NULL;
  487.         }
  488.     ignoreints();
  489. #ifdef OSK
  490.     if ( finptr) {
  491.         ffclose( finptr );
  492.         finptr = NULL;
  493.     }
  494. #endif
  495.         if (Rename(newRCSfilename,RCSfilename)<0) {
  496.                 error("Can't write new RCS file %s; saved in %s",
  497.                       RCSfilename,newRCSfilename);
  498.                 newRCSfilename[0]='\0'; /* avoid deletion by cleanup*/
  499.                 restoreints();
  500.                 VOID cleanup();
  501.                 break;
  502.         }
  503.         newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
  504.  
  505.     newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~022;
  506.     /* newRCSmode is also used to adjust mode of working file for -u and -l */
  507.     if (chmod(RCSfilename,newRCSmode)<0)
  508.                 warn("Can't set mode of %s",RCSfilename);
  509.  
  510.         restoreints();
  511. #       ifdef SNOOPFILE
  512.         logcommand("ci",&newdelta,gendeltas,caller);
  513. #       endif
  514.  
  515.         if (!keepworkingfile) {
  516.                 VOID unlink(workfilename); /* get rid of old file */
  517.         } else {
  518.                 /* expand keywords in file */
  519.                 newworkfilename=
  520.                 xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
  521.                 if (!newworkfilename) continue; /* expand failed */
  522.         ignoreints();
  523.         if (Rename(newworkfilename,workfilename) <0) {
  524.                     error("Can't expand keywords in %s",workfilename);
  525.                     restoreints();
  526.                     continue;
  527.                 }
  528.         newworkfilename[0]='\0'; /* avoid re-unlink by cleanup */
  529.         if (chmod(workfilename, WORKMODE(newRCSmode))<0)
  530.                     warn("Can't adjust mode of %s",workfilename);
  531.                 restoreints();
  532.         }
  533.         diagnose("done");
  534.  
  535.         } while (cleanup(),
  536.                  ++argv, --argc >=1);
  537.  
  538.         exit(nerror!=0);
  539.     /*NOTREACHED*/
  540. }       /* end of main (ci) */
  541. /*****************************************************************/
  542. /* the rest are auxiliary routines                               */
  543.  
  544.  
  545. int addelta()
  546. /* Function: Appends a delta to the delta tree, whose number is
  547.  * given by newdelnum[]. Updates Head, newdelnum, newdenumlength,
  548.  * olddeltanum and the links in newdelta.
  549.  * Retruns false on error, true on success.
  550.  */
  551. {
  552.         register char * sp, * tp;
  553.         register int i;
  554.  
  555.         newdnumlength=countnumflds(newdelnum);
  556.  
  557.         if (initflag || rcsinitflag ) {
  558.                 /* this covers non-existing RCS file and a file initialized with rcs -i */
  559.         if ((newdnumlength==0)&&(Dbranch!=nil)) {
  560.             VOID strcpy(newdelnum,Dbranch->num);
  561.             newdnumlength=countnumflds(newdelnum);
  562.         }
  563.                 if (newdnumlength==0) VOID strcpy(newdelnum,"1.1");
  564.                 elsif (newdnumlength==1) VOID strcat(newdelnum,".1");
  565.                 elsif (newdnumlength>2) {
  566.                     error("Branch point does not exist for %s",newdelnum);
  567.                     return false;
  568.                 } /* newdnumlength == 2 is OK;  */
  569.                 olddeltanum=nil;
  570.                 Head = &newdelta;
  571.                 newdelta.next=nil;
  572.                 return true;
  573.         }
  574.         if (newdnumlength==0) {
  575.                 /* derive new revision number from locks */
  576.         targetdelta=findlock(caller,true); /*find and delete it*/
  577.                 if (targetdelta) {
  578.                     /* found an old lock */
  579.                     olddeltanum=targetdelta->num;
  580.                     /* check whether locked revision exists */
  581.                     if (!genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas)) return false;
  582.                     if (targetdelta==Head) {
  583.                         /* make new head */
  584.                         newdelta.next=Head;
  585.                         Head= &newdelta;
  586.                         incnum(olddeltanum, newdelnum);
  587.                     } elsif ((targetdelta->next==nil)&&(countnumflds(olddeltanum)>2)) {
  588.                         /* new tip revision on side branch */
  589.                         targetdelta->next= &newdelta;
  590.                         newdelta.next = nil;
  591.                         incnum(olddeltanum, newdelnum);
  592.                     } else {
  593.                         /* middle revision; start a new branch */
  594.                         newdelnum[0]='\0';
  595.                         if (!addbranch(targetdelta,newdelnum)) return false;
  596.                     }
  597.                     return true; /* successfull use of existing lock */
  598.                 } else {
  599.                     /* no existing lock; try Dbranch */
  600.                     /* update newdelnum */
  601.                     if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
  602.                         error("no lock set by %s",caller);
  603.                         return false;
  604.                     }
  605.                     if (Dbranch) {
  606.                         VOID strcpy(newdelnum,Dbranch->num);
  607.                     } else {
  608.                         incnum(Head->num,newdelnum);
  609.                     }
  610.                     newdnumlength=countnumflds(newdelnum);
  611.                     /* now fall into next statement */
  612.                 }
  613.         }
  614.         if (newdnumlength<=2) {
  615.                 /* add new head per given number */
  616.                 olddeltanum=Head->num;
  617.                 if(newdnumlength==1) {
  618.                     /* make a two-field number out of it*/
  619.                     if (cmpnumfld(newdelnum,olddeltanum,1)==0)
  620.                           incnum(olddeltanum,newdelnum);
  621.                     else  VOID strcat(newdelnum, ".1");
  622.                 }
  623.                 if (cmpnum(newdelnum,olddeltanum) <= 0) {
  624.                     error("deltanumber %s too low; must be higher than %s",
  625.                           newdelnum,Head->num);
  626.                     return false;
  627.                 }
  628.                 if (!(targetdelta=removelock(caller,Head))) return false;
  629.                 if (!(genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas))) return false;
  630.                 newdelta.next=Head;
  631.                 Head= &newdelta;
  632.         } else {
  633.                 /* put new revision on side branch */
  634.                 /*first, get branch point */
  635.                 tp=branchpointnum; sp=newdelnum;
  636.                 for(i=newdnumlength-(newdnumlength%2==1?1:2);i>0;i--) {
  637.                     while (*sp != '.') *tp++ = *sp++; /*copy field*/
  638.                     *tp++ = *sp++;                    /*copy dot  */
  639.                 }
  640.                 *(tp-1) = '\0'; /* kill final dot */
  641.                 olddeltanum=branchpointnum; /*temporary old delta*/
  642.                 if (!(targetdelta=genrevs(branchpointnum,(char *)nil,(char *)nil,(char *)nil,gendeltas)))
  643.                      return false;
  644.                 if (cmpnum(targetdelta->num,branchpointnum)!=0) {
  645.                     error("Cannot find branchpoint %s",branchpointnum);
  646.                     return false;
  647.                 }
  648.                 if (!addbranch(targetdelta,newdelnum)) return false;
  649.         }
  650.         return true;
  651. }
  652.  
  653.  
  654.  
  655. int addbranch(branchpoint,num)
  656. struct hshentry * branchpoint;
  657. char * num;
  658. /* adds a new branch and branch delta at branchpoint.
  659.  * If num is the null string, appends the new branch, incrementing
  660.  * the highest branch number (initially 1), and setting the level number to 1.
  661.  * the new delta and branchhead are in globals newdelta and newbranch, resp.
  662.  * the new number is placed into num.
  663.  * returns false on error.
  664.  */
  665. {
  666.         struct branchhead * bhead, * btrail;
  667.         char branchnum[revlength];
  668.         int numlength, result, field;
  669.  
  670.         numlength = countnumflds(num);
  671.  
  672.         if (branchpoint->branches==nil) {
  673.                 /* start first branch */
  674.                 branchpoint->branches = &newbranch;
  675.                 if (numlength==0) {
  676.                         VOID strcpy(num, branchpoint->num);
  677.                         VOID strcat(num,".1.1");
  678.                 } elsif(countnumflds(num)%2 == 1)
  679.                         VOID strcat(num, ".1");
  680.                 newbranch.nextbranch=nil;
  681.  
  682.         } elsif (numlength==0) {
  683.                 /* append new branch to the end */
  684.                 bhead=branchpoint->branches;
  685.                 while (bhead->nextbranch) bhead=bhead->nextbranch;
  686.                 bhead->nextbranch = &newbranch;
  687.                 getbranchno(bhead->hsh->num,branchnum);
  688.                 incnum(branchnum,num);
  689.                 VOID strcat(num,".1");
  690.                 newbranch.nextbranch=nil;
  691.         } else {
  692.                 /* place the branch properly */
  693.                 field = numlength - (numlength%2 ==1?0:1);
  694.                 /* field of branch number */
  695.                 bhead=branchpoint->branches;
  696.                 while ((bhead!=nil) &&
  697.                        ((result=cmpnumfld(num,bhead->hsh->num,field))>0)) {
  698.                         btrail=bhead;
  699.                         bhead=bhead->nextbranch;
  700.                 }
  701.                 if (bhead==nil || result<0) {
  702.                         /* insert/append new branchhead */
  703.                         if (bhead==branchpoint->branches)
  704.                                 branchpoint->branches= &newbranch;
  705.                         else    btrail->nextbranch= &newbranch;
  706.                         newbranch.nextbranch=bhead;
  707.                         if (numlength%2 ==1) VOID strcat(num,".1");
  708.                 } else {
  709.                         /* branch exists; append to end */
  710.                         getbranchno(num,branchnum);
  711.                         if (!(targetdelta=genrevs(branchnum,(char *)nil,(char *)nil,(char *)nil,
  712.                                 gendeltas))) return false;
  713.                         olddeltanum=targetdelta->num;
  714.                         if (cmpnum(num,olddeltanum) <= 0) {
  715.                                 error("deltanumber %s too low; must be higher than %s",
  716.                                       num,olddeltanum);
  717.                                 return false;
  718.                         }
  719.                         if (!removelock(caller,targetdelta)) return false;
  720.                         if (numlength%2==1) incnum(olddeltanum,num);
  721.                         targetdelta->next= &newdelta;
  722.                         newdelta.next=nil;
  723.                         return true; /* Don't do anything to newbranch */
  724.                 }
  725.         }
  726.         newbranch.hsh = &newdelta;
  727.         newdelta.next=nil;
  728.         return true;
  729. }
  730.  
  731.  
  732.  
  733. struct hshentry * removelock(who,delta)
  734. char * who; struct hshentry * delta;
  735. /* function: Finds the lock held by who on delta,
  736.  * removes it, and returns a pointer to the delta.
  737.  * Prints an error message and returns nil if there is no such lock.
  738.  * An exception is if StrictLocks==false, and who is the owner of
  739.  * the RCS file. If who does not have a lock in this case,
  740.  * delta is returned.
  741.  */
  742. {
  743.         register struct lock * next, * trail;
  744.         char * num;
  745.         struct lock dummy;
  746.         int whomatch, nummatch;
  747.  
  748.         num=delta->num;
  749.         dummy.nextlock=next=Locks;
  750.         trail = &dummy;
  751.         while (next!=nil) {
  752.                 whomatch=strcmp(who,next->login);
  753.                 nummatch=strcmp(num,next->delta->num);
  754.                 if ((whomatch==0) && (nummatch==0)) break;
  755.                      /*found a lock on delta by who*/
  756.                 if ((whomatch!=0)&&(nummatch==0)) {
  757.                     error("revision %s locked by %s",num,next->login);
  758.                     return nil;
  759.                 }
  760.                 trail=next;
  761.                 next=next->nextlock;
  762.         }
  763.         if (next!=nil) {
  764.                 /*found one; delete it */
  765.                 trail->nextlock=next->nextlock;
  766.                 Locks=dummy.nextlock;
  767.                 next->delta->lockedby=nil; /* reset locked-by */
  768.                 return next->delta;
  769.         } else {
  770.                 if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
  771.                     error("no lock set by %s for revision %s",who,num);
  772.                     return nil;
  773.                 } else {
  774.                         return delta;
  775.                 }
  776.         }
  777. }
  778.  
  779.  
  780.  
  781. char * getdate()
  782. /* Function: returns a pointer to the current date in the form
  783.  * YY.MM.DD.hh.mm.ss\0
  784.  */
  785. {
  786.         long clock;
  787.         struct tm * tm;
  788.     static char buffer[datelength]; /* date buffer */
  789.         clock=time((long *)0);
  790.         tm=localtime(&clock);
  791.         VOID sprintf(buffer, DATEFORM,
  792.                 tm->tm_year, tm->tm_mon+1, tm->tm_mday,
  793.                 tm->tm_hour, tm->tm_min, tm->tm_sec);
  794.         return buffer;
  795. }
  796.  
  797.  
  798. char * xpandfile (unexfname,dir,delta)
  799. char * unexfname, * dir;
  800. struct hshentry * delta;
  801. /* Function: Reads file unexpfname and copies it to a
  802.  * file in dir, performing keyword substitution with data from delta.
  803.  * returns the name of the expanded file if successful, nil otherwise.
  804.  */
  805. {       char * targetfname;
  806.         FILE * unexfile, *exfile;
  807.  
  808.         targetfname=mktempfile(dir,TMPFILE3);
  809.         if ((unexfile=fopen(unexfname,  "r" ))==NULL ||
  810.             (exfile  =fopen(targetfname,"w"))==NULL) {
  811.                 error("Can't expand file %s",unexfname);
  812.                 return nil;
  813.         }
  814.         while (expandline(unexfile,exfile,delta,false,false)); /*expand*/
  815.         ffclose(unexfile);ffclose(exfile);
  816.         return targetfname;
  817. }
  818.  
  819.  
  820. mustcheckin (unexfname,delta)
  821. char * unexfname; struct hshentry * delta;
  822. /* Function: determines whether checkin should proceed.
  823.  * Compares the wrkfilename with unexfname, disregarding keywords.
  824.  * If the 2 files differ, returns true. If they do not differ, asks the user
  825.  * whether to return true or false (i.e., whether to checkin the file anyway.
  826.  * If the files do not differ, and quietflag==true, returns false.
  827.  * Shortcut: If forceciflag==true, mustcheckin() always returns true.
  828.  */
  829. {       register int c;
  830.         int response, result;
  831.  
  832.         if (forceciflag) return true;
  833.  
  834.         if (!rcsfcmp(workfilename,unexfname,delta)) return true;
  835.         /* If files are different, must check them in. */
  836.  
  837.         /* files are the same */
  838.         diagnose("File %s is unchanged with respect to revision %s",
  839.                 workfilename,delta->num);
  840.         if (quietflag || !isatty(fileno(stdin))) {
  841.                 /* Files are the same, but can't ask, so don't checkin*/
  842.                 result=false;
  843.         } else {
  844.                 /* ask user whether to check in */
  845.                 VOID fputs("checkin anyway? [ny](n): ",stderr);
  846.                 fflush(stderr); response=c=getchar();
  847.                 while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
  848.                 result=(response=='y'||response=='Y');
  849.         }
  850.         if (result==false) {
  851.                 if (quietflag) {
  852.                     warn("checkin aborted since %s was not changed; %s %sdeleted.",
  853.                              workfilename,workfilename,keepworkingfile?"not ":"");
  854.                 } else {
  855.                     diagnose("checkin aborted; %s %sdeleted.",
  856.                              workfilename,keepworkingfile?"not ":"");
  857.                 }
  858.                 if (!keepworkingfile) VOID unlink(workfilename);
  859.         }
  860.         return result;
  861. }
  862.  
  863.  
  864.  
  865.  
  866. /* --------------------- G E T L O G M S G --------------------------------*/
  867. extern int stdinread; /* is >0 if redirected stdin has been read once.     */
  868.  
  869.  
  870. char * getlogmsg()
  871. /* Function: obtains a log message and returns a pointer to it.
  872.  * If a log message is given via the -m option, a pointer to that
  873.  * string is returned.
  874.  * If this is the initial revision, a standard log message is returned.
  875.  * Otherwise, reads a character string from the terminal.
  876.  * The string must be terminated with a control-d or a single '.' on a
  877.  * line. getlogmsg prompts the first time it is called for the
  878.  * log message; during all later calls it asks whether the previous
  879.  * log message can be reused.
  880.  * returns a pointer to the character string; the pointer is always non-nil.
  881.  */
  882. {
  883.         static logyet = false;      /*indicates whether previous log present*/
  884.         static char emptylog[]  = "*** empty log message ***\n";
  885.         static char initiallog[]= "Initial revision\n";
  886.         char response;
  887.     int cin;
  888.         register char c, old1, old2, * tp;
  889.  
  890.         if (msg) return msg;
  891.  
  892.         if ((olddeltanum==nil)&&
  893.         ((cmpnum(newdelnum,"1.1")==0)||(cmpnum(newdelnum,"1.0")==0))) {
  894.                 return initiallog;
  895.     }
  896.     if (keepflag) {
  897.         /* generate std. log message */
  898.         VOID sprintf(logmsg, "checked in with -k by %s at %s.\n",caller,getdate());
  899.         return(logmsg);
  900.     }
  901.         if (logyet) {
  902.                 /*previous log available*/
  903.                 if (!isatty(fileno(stdin))) return logmsg; /* reuse if stdin is not a terminal*/
  904.                 /* otherwise ask */
  905.         cleareof(stdin);clearerr(stdin);    /* reset EOF ptr */
  906.         VOID fputs("reuse log message of previous file? [yn](y): ",stderr);
  907.                 fflush(stderr);cin=getchar();
  908.         response=cin;
  909.                 while (!(cin==EOF || cin=='\n')) cin=getchar();/*skip to end of line*/
  910.                 if (response=='\n'||response=='y'||response=='Y')
  911.                         return logmsg;
  912.                 else
  913.                         logmsg[0]='\0'; /*kill existing log message */
  914.         }
  915.  
  916.         /* now read string from stdin */
  917.         if (isatty(fileno(stdin))) {
  918.                 VOID fputs("enter log message:\n(terminate with <eof> or single '.')\n>> ",stderr);
  919.                 fflush(stderr);
  920.         } else {  /* redirected stdin */
  921.                 if (stdinread>0)
  922.                     faterror("Can't reread redirected stdin for log message; use -m");
  923.                 stdinread++;
  924.         }
  925.  
  926.     tp=logmsg; old1='\n'; old2=' ';
  927.     if (feof(stdin))
  928.         cleareof(stdin),clearerr(stdin);
  929.         for (;;) {
  930.                 cin=getchar();
  931.                 if (cin==EOF) {
  932.                         if(isatty(fileno(stdin))) VOID putc('\n',stderr);
  933.             if ((tp==logmsg)||(*(tp-1)!='\n')) *tp++ = '\n'; /* append newline */
  934.                         *tp = '\0'; /*terminate*/
  935.                         break;
  936.                 }
  937.                 if (cin=='\n' && old1=='.' && old2=='\n') {
  938.                         *(tp-1) = '\0'; /*kill last period */
  939.                         break;
  940.                 }
  941.                 if (tp>=logmsg+logsize-2) { /* overflow */
  942.                         if (!isatty(fileno(stdin))) {
  943.                                 warn("log message truncated to %d characters",logsize);
  944.                                 logmsg[logsize-2]='\n';logmsg[logsize-1]='\0';
  945.                                 return logmsg;
  946.                         }
  947.                         VOID fprintf(stderr,"log message too long. Maximum: %d\n",logsize);
  948.                         VOID fputs("reenter log message:\n>> ",stderr);
  949.                         fflush(stderr);
  950.                         tp=logmsg; old1='\n'; old2=' ';
  951.                         while (cin!='\n') cin=getchar(); /*skip line */
  952.                         continue;
  953.                 }
  954.                 if (cin=='\n' && isatty(fileno(stdin))) {
  955.                     VOID fputs(">> ",stderr);
  956.                     fflush(stderr);
  957.                 }
  958.                 *tp++ = cin; old2=old1; old1=cin; /* this is the actual work!*/
  959.                 /*SDELIM will be changed to double SDELIM by putdtext*/
  960.         } /* end for */
  961.  
  962.         /* now check whether the log message is not empty */
  963.         tp=logmsg;
  964.         while ((c= *tp++)==' '||c=='\t'||c=='\n'||c=='\f');
  965.         if (*tp=='\0') {
  966.                 logyet=false;
  967.                 return emptylog;
  968.         } else {
  969.                 logyet=true;
  970.                 return logmsg;
  971.         }
  972. }
  973.  
  974.