home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / cool / cool.lha / ice / pisces / rcs / rcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-04  |  51.3 KB  |  1,570 lines

  1. /*
  2.  *                      RCS create/change operation
  3.  */
  4. #ifndef lint
  5. static char rcsid[]=
  6. "$Header: /tan/u1/neath/pisces/rcs/RCS/rcs.c,v 1.1 90/05/21 13:42:56 neath Exp $ Purdue CS";
  7. #endif
  8. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  9.    Distributed under license by the Free Software Foundation, Inc.
  10.  
  11. This file is part of RCS.
  12.  
  13. RCS is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 1, or (at your option)
  16. any later version.
  17.  
  18. RCS is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. GNU General Public License for more details.
  22.  
  23. You should have received a copy of the GNU General Public License
  24. along with RCS; see the file COPYING.  If not, write to
  25. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  26.  
  27. Report problems and direct all questions to:
  28.  
  29.     rcs-bugs@cs.purdue.edu
  30.  
  31. */
  32.  
  33.  
  34.  
  35.  
  36. /* $Log:    rcs.c,v $
  37.  * Revision 1.1  90/05/21  13:42:56  neath
  38.  * Initial revision
  39.  * 
  40.  * Revision 4.11  89/05/01  15:12:06  narten
  41.  * changed copyright header to reflect current distribution rules
  42.  * 
  43.  * Revision 4.10  88/11/08  16:01:54  narten
  44.  * didn't install previous patch correctly
  45.  * 
  46.  * Revision 4.9  88/11/08  13:56:01  narten
  47.  * removed include <sysexits.h> (not needed)
  48.  * minor fix for -A option
  49.  * 
  50.  * Revision 4.8  88/11/08  12:01:58  narten
  51.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  52.  * 
  53.  * Revision 4.8  88/08/09  19:12:27  eggert
  54.  * Don't access freed storage.
  55.  * Use execv(), not system(); yield proper exit status; remove lint.
  56.  * 
  57.  * Revision 4.7  87/12/18  11:37:17  narten
  58.  * lint cleanups (Guy Harris)
  59.  * 
  60.  * Revision 4.6  87/10/18  10:28:48  narten
  61.  * Updating verison numbers. Changes relative to 1.1 are actually 
  62.  * relative to 4.3
  63.  * 
  64.  * Revision 1.4  87/09/24  13:58:52  narten
  65.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  66.  * warnings)
  67.  * 
  68.  * Revision 1.3  87/03/27  14:21:55  jenkins
  69.  * Port to suns
  70.  * 
  71.  * Revision 1.2  85/12/17  13:59:09  albitz
  72.  * Changed setstate to rcs_setstate because of conflict with random.o.
  73.  * 
  74.  * Revision 1.1  84/01/23  14:50:09  kcs
  75.  * Initial revision
  76.  * 
  77.  * Revision 4.3  83/12/15  12:27:33  wft
  78.  * rcs -u now breaks most recent lock if it can't find a lock by the caller.
  79.  * 
  80.  * Revision 4.2  83/12/05  10:18:20  wft
  81.  * Added conditional compilation for sending mail.
  82.  * Alternatives: V4_2BSD, V6, USG, and other.
  83.  * 
  84.  * Revision 4.1  83/05/10  16:43:02  wft
  85.  * Simplified breaklock(); added calls to findlock() and getcaller().
  86.  * Added option -b (default branch). Updated -s and -w for -b.
  87.  * Removed calls to stat(); now done by pairfilenames().
  88.  * Replaced most catchints() calls with restoreints().
  89.  * Removed check for exit status of delivermail().
  90.  * Directed all interactive output to stderr.
  91.  * 
  92.  * Revision 3.9.1.1  83/12/02  22:08:51  wft
  93.  * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
  94.  * 
  95.  * Revision 3.9  83/02/15  15:38:39  wft
  96.  * Added call to fastcopy() to copy remainder of RCS file.
  97.  *
  98.  * Revision 3.8  83/01/18  17:37:51  wft
  99.  * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
  100.  *
  101.  * Revision 3.7  83/01/15  18:04:25  wft
  102.  * Removed putree(); replaced with puttree() in rcssyn.c.
  103.  * Combined putdellog() and scanlogtext(); deleted putdellog().
  104.  * Cleaned up diagnostics and error messages. Fixed problem with
  105.  * mutilated files in case of deletions in 2 files in a single command.
  106.  * Changed marking of selector from 'D' to DELETE.
  107.  *
  108.  * Revision 3.6  83/01/14  15:37:31  wft
  109.  * Added ignoring of interrupts while new RCS file is renamed;
  110.  * Avoids deletion of RCS files by interrupts.
  111.  *
  112.  * Revision 3.5  82/12/10  21:11:39  wft
  113.  * Removed unused variables, fixed checking of return code from diff,
  114.  * introduced variant COMPAT2 for skipping Suffix on -A files.
  115.  *
  116.  * Revision 3.4  82/12/04  13:18:20  wft
  117.  * Replaced getdelta() with gettree(), changed breaklock to update
  118.  * field lockedby, added some diagnostics.
  119.  *
  120.  * Revision 3.3  82/12/03  17:08:04  wft
  121.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  122.  * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
  123.  * fixed -u for missing revno. Disambiguated structure members.
  124.  *
  125.  * Revision 3.2  82/10/18  21:05:07  wft
  126.  * rcs -i now generates a file mode given by the umask minus write permission;
  127.  * otherwise, rcs keeps the mode, but removes write permission.
  128.  * I added a check for write error, fixed call to getlogin(), replaced
  129.  * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
  130.  * conflicting, long identifiers.
  131.  *
  132.  * Revision 3.1  82/10/13  16:11:07  wft
  133.  * fixed type of variables receiving from getc() (char -> int).
  134.  */
  135.  
  136.  
  137. #include <sys/types.h>
  138. #include <sys/stat.h>
  139. #include "rcsbase.h"
  140. #ifndef lint
  141. static char rcsbaseid[] = RCSBASE;
  142. #endif
  143.  
  144. extern FILE * fopen();
  145. extern char * bindex();
  146. extern int  expandsym();                /* get numeric revision name        */
  147. extern struct  hshentry  * getnum();
  148. extern struct  lock      * addlock();   /* add a lock                       */
  149. extern char              * getid();
  150. extern char              Klog[], Khead[], Kaccess[], Kbranch[], Ktext[];
  151. #ifdef COMPAT2
  152. extern char Ksuffix[];
  153. #endif
  154. extern char * getcaller();              /* get login of caller              */
  155. extern struct hshentry   * findlock();  /* find and remove lock             */
  156. extern struct hshentry   * genrevs();
  157. extern char * checkid();                /* check an identifier              */
  158. extern char * getfullRCSname();         /* get full path name of RCS file   */
  159. extern char * mktempfile();             /* temporary file name generator    */
  160. extern free();
  161. extern void catchints();
  162. extern void ignoreints();
  163. extern int  nerror;                     /* counter for errors               */
  164. extern int  quietflag;                  /* diagnoses suppressed if true     */
  165. extern char curlogmsg[];                /* current log message              */
  166. extern char * resultfile;               /* filename for fcopy            */
  167. extern FILE *fcopy;                     /* result file during editing       */
  168. extern FILE * finptr;                   /* RCS input file                   */
  169. extern FILE * frewrite;                 /* new RCS file                     */
  170. extern int    rewriteflag;              /* indicates whether input should be*/
  171.                     /* echoed to frewrite               */
  172.  
  173. char * newRCSfilename, * diffilename, * cutfilename;
  174. char * RCSfilename, * workfilename;
  175. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  176. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  177.  
  178. char accessorlst[strtsize];
  179. FILE * fcut;        /* temporary file to rebuild delta tree                 */
  180. int    oldumask;    /* save umask                                           */
  181.  
  182. int initflag, strictlock, strict_selected, textflag;
  183. char * textfile, * accessfile;
  184. char * caller, numrev[30];            /* caller's login;               */
  185. struct  access  * newaccessor,  * rmvaccessor,  * rplaccessor;
  186. struct  access  *curaccess,  *rmaccess;
  187. struct  hshentry        * gendeltas[hshsize];
  188.  
  189. struct  Lockrev {
  190.         char    * revno;
  191.         struct  Lockrev   * nextrev;
  192. };
  193.  
  194. struct  Symrev {
  195.         char    * revno;
  196.         char    * ssymbol;
  197.         int     override;
  198.         struct  Symrev  * nextsym;
  199. };
  200.  
  201. struct  Status {
  202.         char    * revno;
  203.         char    * status;
  204.         struct  Status  * nextstatus;
  205. };
  206.  
  207. struct delrevpair {
  208.         char    * strt;
  209.         char    * end;
  210.         int     code;
  211. };
  212.  
  213. struct  Lockrev * newlocklst,   * rmvlocklst;
  214. struct  Symrev  * assoclst,  * lastassoc;
  215. struct  Status  * statelst,  * laststate;
  216. struct  delrevpair      * delrev;
  217. struct  hshentry        * cuthead,  *cuttail,  * delstrt;
  218. char    branchnum[revlength], * branchsym;
  219. struct  hshentry branchdummy;
  220. char    * commsyml;
  221. char    * headstate;
  222. int     lockhead,unlockcaller,chgheadstate,branchflag,commentflag;
  223. int     delaccessflag;
  224. enum    stringwork {copy, edit, empty}; /* expand and edit_expand not needed */
  225.  
  226.  
  227. main (argc, argv)
  228. int argc;
  229. char * argv[];
  230. {
  231.         char    *comdusge;
  232.         int     result;
  233.     struct    access    *removeaccess(),  * getaccessor();
  234.         struct  Lockrev *rmnewlocklst();
  235.         struct  Lockrev *curlock,  * rmvlock, *lockpt;
  236.         struct  Status  * curstate;
  237.         struct  access    *temp, *temptr;
  238.     int status;
  239.  
  240.     status = 0;
  241.         nerror = 0;
  242.     catchints();
  243.         cmdid = "rcs";
  244.         quietflag = false;
  245.         comdusge ="command format:\nrcs -i -alogins -Alogins -e[logins] -b[rev] -c[commentleader] -l[rev] -u[rev] -L -U -nname[:rev] -Nname[:rev] -orange -sstate[:rev] -t[textfile] file....";
  246.         rplaccessor = nil;     delstrt = nil;
  247.         accessfile = textfile = caller = nil;
  248.         branchflag = commentflag = chgheadstate = false;
  249.         lockhead = false; unlockcaller=false;
  250.         initflag= textflag = false;
  251.         strict_selected = 0;
  252.  
  253.     caller=getcaller();
  254.         laststate = statelst = nil;
  255.         lastassoc = assoclst = nil;
  256.         curlock = rmvlock = newlocklst = rmvlocklst = nil;
  257.         curaccess = rmaccess = rmvaccessor = newaccessor = nil;
  258.         delaccessflag = false;
  259.  
  260.         /*  preprocessing command options    */
  261.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  262.                 switch ((*argv)[1]) {
  263.  
  264.                 case 'i':   /*  initail version  */
  265.                         initflag = true;
  266.                         break;
  267.  
  268.                 case 'b':  /* change default branch */
  269.                         if (branchflag)warn("Redfinition of option -b");
  270.                         branchflag= true;
  271.                         branchsym = (*argv)+2;
  272.                         break;
  273.  
  274.                 case 'c':   /*  change comment symbol   */
  275.                         if (commentflag)warn("Redefinition of option -c");
  276.                         commentflag = true;
  277.                         commsyml = (*argv)+2;
  278.                         break;
  279.  
  280.                 case 'a':  /*  add new accessor   */
  281.                         if ( (*argv)[2] == '\0') {
  282.                             error("Login name missing after -a");
  283.                         }
  284.                         if ( (temp = getaccessor((*argv)+1)) ) {
  285.                             if ( newaccessor )
  286.                                 curaccess->nextaccess = temp->nextaccess;
  287.                             else
  288.                                 newaccessor = temp->nextaccess;
  289.                             temp->nextaccess = nil;
  290.                             curaccess = temp;
  291.                         }
  292.                         break;
  293.  
  294.                 case 'A':  /*  append access list according to accessfile  */
  295.                         if ( (*argv)[2] == '\0') {
  296.                             error("Missing file name after -A");
  297.                             break;
  298.                         }
  299.                         if ( accessfile) warn("Redefinition of option -A");
  300.                         *argv = *argv+2;
  301.                         if( pairfilenames(1, argv, true, false) > 0) {
  302.                             releaselst(newaccessor);
  303.                             newaccessor = curaccess = nil;
  304.                             releaselst(rmvaccessor);
  305.                             rmvaccessor = rmaccess = nil;
  306.                             accessfile = RCSfilename;
  307.                         }
  308.                         else
  309.                             accessfile = nil;
  310.                         break;
  311.  
  312.                 case 'e':    /*  remove accessors   */
  313.                         if ( (*argv)[2] == '\0' ) {
  314.                             delaccessflag = true;
  315.                             break;
  316.                         }
  317.                         if ( (temp = getaccessor((*argv)+1))  ) {
  318.                             if ( rmvaccessor )
  319.                                 rmaccess->nextaccess = temp->nextaccess;
  320.                             else
  321.                                 rmvaccessor = temp->nextaccess;
  322.                             temptr = temp->nextaccess;
  323.                             temp->nextaccess = nil;
  324.                             rmaccess = temp;
  325.                             while( temptr ) {
  326.                                 newaccessor = removeaccess(temptr,newaccessor,false);
  327.                                 temptr = temptr->nextaccess;
  328.                             }
  329.                             curaccess = temp = newaccessor;
  330.                             while( temp){
  331.                                 curaccess = temp;
  332.                                 temp = temp->nextaccess;
  333.                             }
  334.                         }
  335.                         break;
  336.  
  337.                 case 'l':    /*   lock a revision if it is unlocked   */
  338.                         if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
  339.                             lockhead = true;
  340.                             break;
  341.                         }
  342.                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
  343.                         lockpt->revno = (*argv)+2;
  344.                         lockpt->nextrev = nil;
  345.                         if ( curlock )
  346.                             curlock->nextrev = lockpt;
  347.                         else
  348.                             newlocklst = lockpt;
  349.                         curlock = lockpt;
  350.                         break;
  351.  
  352.                 case 'u':   /*  release lock of a locked revision   */
  353.                         if ( (*argv)[2] == '\0'){ /*  unlock head  */
  354.                             unlockcaller=true;
  355.                             break;
  356.                         }
  357.                         lockpt = (struct Lockrev *)talloc(sizeof(struct Lockrev));
  358.                         lockpt->revno = (*argv)+2;
  359.                         lockpt->nextrev = nil;
  360.                         if (rmvlock)
  361.                             rmvlock->nextrev = lockpt;
  362.                         else
  363.                             rmvlocklst = lockpt;
  364.                         rmvlock = lockpt;
  365.  
  366.                         curlock = rmnewlocklst(lockpt);
  367.                         break;
  368.  
  369.                 case 'L':   /*  set strict locking */
  370.                         if (strict_selected++) {  /* Already selected L or U? */
  371.                if (!strictlock)      /* Already selected -U? */
  372.                    warn("Option -L overrides -U");
  373.                         }
  374.                         strictlock = true;
  375.                         break;
  376.  
  377.                 case 'U':   /*  release strict locking */
  378.                         if (strict_selected++) {  /* Already selected L or U? */
  379.                if (strictlock)      /* Already selected -L? */
  380.                    warn("Option -L overrides -U");
  381.                         }
  382.             else
  383.                 strictlock = false;
  384.                         break;
  385.  
  386.                 case 'n':    /*  add new association: error, if name exists */
  387.                         if ( (*argv)[2] == '\0') {
  388.                             error("Missing symbolic name after -n");
  389.                             break;
  390.                         }
  391.                         getassoclst(false, (*argv)+1);
  392.                         break;
  393.  
  394.                 case 'N':   /*  add or change association   */
  395.                         if ( (*argv)[2] == '\0') {
  396.                             error("Missing symbolic name after -N");
  397.                             break;
  398.                         }
  399.                         getassoclst(true, (*argv)+1);
  400.                         break;
  401.  
  402.                 case 'o':   /*  delete revisins  */
  403.                         if (delrev) warn("Redefinition of option -o");
  404.                         if ( (*argv)[2] == '\0' ) {
  405.                             error("Missing revision range after -o");
  406.                             break;
  407.                         }
  408.                         getdelrev( (*argv)+1 );
  409.                         break;
  410.  
  411.                 case 's':   /*  change state attribute of a revision  */
  412.                         if ( (*argv)[2] == '\0') {
  413.                             error("State missing after -s");
  414.                             break;
  415.                         }
  416.                         getstates( (*argv)+1);
  417.                         break;
  418.  
  419.                 case 't':   /*  change descriptive text   */
  420.                         textflag=true;
  421.                         if ((*argv)[2]!='\0'){
  422.                                 if (textfile!=nil)warn("Redefinition of -t option");
  423.                                 textfile = (*argv)+2;
  424.                         }
  425.                         break;
  426.  
  427.                 case 'q':
  428.                         quietflag = true;
  429.                         break;
  430.                 default:
  431.                         faterror("Unknown option: %s\n%s", *argv, comdusge);
  432.                 };
  433.         }  /* end processing of options */
  434.  
  435.         if (argc<1) faterror("No input file\n%s", comdusge);
  436.         if (nerror) {   /*  exit, if any error in command options  */
  437.             diagnose("%s aborted",cmdid);
  438.             exit(1);
  439.         }
  440.         if (accessfile) /*  get replacement for access list   */
  441.             getrplaccess();
  442.         if (nerror) {
  443.             diagnose("%s aborted",cmdid);
  444.             exit(1);
  445.         }
  446.  
  447.         /* now handle all filenames */
  448.         do {
  449.         rewriteflag = false;
  450.         finptr=frewrite=NULL;
  451.  
  452.         if ( initflag ) {
  453.             switch( pairfilenames(argc, argv, false, false) ) {
  454.                 case -1: break;        /*  not exist; ok */
  455.                 case  0: continue;     /*  error         */
  456.                 case  1: error("file %s exists already", RCSfilename);
  457.                          VOID fclose(finptr);
  458.                          continue;
  459.             }
  460.     }
  461.         else  {
  462.             switch( pairfilenames(argc, argv, true, false) ) {
  463.                 case -1: continue;    /*  not exist      */
  464.                 case  0: continue;    /*  errors         */
  465.                 case  1: break;       /*  file exists; ok*/
  466.             }
  467.     }
  468.  
  469.  
  470.         /* now RCSfilename contains the name of the RCS file, and
  471.          * workfilename contains the name of the working file.
  472.          * if !initflag, finptr contains the file descriptor for the
  473.          * RCS file. The admin node is initialized.
  474.          */
  475.  
  476.         diagnose("RCS file: %s", RCSfilename);
  477.  
  478.         if (!trydiraccess(RCSfilename))            continue; /* give up */
  479.         if (!initflag && !checkaccesslist(caller)) continue; /* give up */
  480.         if (!trysema(RCSfilename,true))            continue; /* give up */
  481.  
  482.         gettree(); /* read in delta tree */
  483.  
  484.         /*  update admin. node    */
  485.         if (strict_selected) StrictLocks = strictlock;
  486.         if (commentflag) Comment = commsyml;
  487.  
  488.         /* update default branch */
  489.         if (branchflag && expandsym(branchsym, branchnum)) {
  490.             if (countnumflds(branchnum)>0) {
  491.                 branchdummy.num=branchnum;
  492.                 Dbranch = &branchdummy;
  493.             } else
  494.                 Dbranch = nil;
  495.         }
  496.  
  497.         /*  update access list   */
  498.         if ( delaccessflag ) AccessList = nil;
  499.         if ( accessfile ) {
  500.             temp = rplaccessor;
  501.             while( temp ) {
  502.                 temptr = temp->nextaccess;
  503.                 if ( addnewaccess(temp) )
  504.                     temp->nextaccess = nil;
  505.                 temp = temptr;
  506.             }
  507.         }
  508.         temp = rmvaccessor;
  509.         while(temp)   {         /*  remove accessors from accesslist   */
  510.             AccessList = removeaccess(temp, AccessList,true);
  511.             temp = temp->nextaccess;
  512.         }
  513.         temp = newaccessor;
  514.         while( temp)  {         /*  add new accessors   */
  515.             temptr = temp->nextaccess;
  516.             if ( addnewaccess( temp ) )
  517.                 temp->nextaccess = nil;
  518.             temp = temptr;
  519.         }
  520.  
  521.         updateassoc();          /*  update association list   */
  522.  
  523.         updatelocks();          /*  update locks              */
  524.  
  525.         /*  update state attribution  */
  526.         if (chgheadstate) {
  527.             /* change state of default branch or head */
  528.             if (Dbranch==nil) {
  529.                 if (Head==nil)
  530.                      warn("Can't change states in an empty tree");
  531.                 else Head->state = headstate;
  532.             } else {
  533.                 rcs_setstate(Dbranch->num,headstate); /* Can't set directly */
  534.             }
  535.         }
  536.         curstate = statelst;
  537.         while( curstate ) {
  538.             rcs_setstate(curstate->revno,curstate->status);
  539.             curstate = curstate->nextstatus;
  540.         }
  541.  
  542.         cuthead = cuttail = nil;
  543.         if ( delrev && removerevs()) {
  544.             /*  rebuild delta tree if some deltas are deleted   */
  545.             if ( cuttail )
  546.         VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
  547.                  (char *)nil, gendeltas);
  548.             buildtree();
  549.         }
  550.  
  551.  
  552.         /* prepare for rewriting the RCS file */
  553.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  554.         oldumask = umask(0222); /* turn off write bits */
  555.         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  556.                 VOID fclose(finptr);
  557.                 error("Can't open file %s",newRCSfilename);
  558.                 continue;
  559.         }
  560.         VOID umask(oldumask);
  561.         putadmin(frewrite);
  562.         if ( Head )
  563.            puttree(Head, frewrite);
  564.     putdesc(initflag,textflag,textfile,quietflag);
  565.         rewriteflag = false;
  566.  
  567.         if ( Head) {
  568.             if (!delrev) {
  569.                 /* no revision deleted */
  570.                 fastcopy(finptr,frewrite);
  571.             } else {
  572.                 if ( cuttail )
  573.                     buildeltatext(gendeltas);
  574.                 else
  575.                     scanlogtext((struct hshentry *)nil,empty);
  576.                     /* copy rest of delta text nodes that are not deleted      */
  577.             }
  578.         }
  579.         ffclose(frewrite);   frewrite = NULL;
  580.         if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
  581.         ignoreints();        /* ignore interrupts */
  582.             if(rename(newRCSfilename,RCSfilename)<0) {
  583.                 error("Can't create RCS file %s; saved in %s",
  584.                    RCSfilename, newRCSfilename);
  585.                 newRCSfilename[0] = '\0';  /*  avoid deletion by cleanup  */
  586.                 restoreints();
  587.                 VOID cleanup();
  588.                 break;
  589.             }
  590.             newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
  591.             /* update mode */
  592.             result=0;
  593.             if (!initflag) /* preserve mode bits */
  594.                 result=chmod(RCSfilename,RCSstat.st_mode & ~0222);
  595.             elsif (haveworkstat==0)  /* initialization, and work file exists */
  596.                 result=chmod(RCSfilename,workstat.st_mode & ~0222);
  597.             if (result<0) warn("Can't set mode of %s",RCSfilename);
  598.  
  599.             restoreints();                /* catch them all again */
  600.             diagnose("done");
  601.         } else {
  602.         diagnose("%s aborted; %s unchanged.",cmdid,RCSfilename);
  603.         status = 1;
  604.         nerror = 0;
  605.         }
  606.         } while (cleanup(),
  607.                  ++argv, --argc >=1);
  608.  
  609.         exit(status);
  610. }       /* end of main (rcs) */
  611.  
  612.  
  613.  
  614. getassoclst(flag, sp)
  615. int     flag;
  616. char    * sp;
  617. /*  Function:   associate a symbolic name to a revision or branch,      */
  618. /*              and store in assoclst                                   */
  619.  
  620. {
  621.         struct   Symrev  * pt;
  622.         char             * temp, *temp2;
  623.         int                c;
  624.  
  625.         while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
  626.         temp = sp;
  627.         temp2=checkid(sp, ':');  /*  check for invalid symbolic name  */
  628.         sp = temp2; c = *sp;   *sp = '\0';
  629.         while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
  630.  
  631.         if ( c != ':' && c != '\0') {
  632.         error("Invalid string %s after option -n or -N",sp);
  633.             return;
  634.         }
  635.  
  636.         pt = (struct Symrev *)talloc(sizeof(struct Symrev));
  637.         pt->ssymbol = temp;
  638.         pt->override = flag;
  639.     if (c == '\0')  /*  delete symbol  */
  640.             pt->revno = nil;
  641.         else {
  642.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  643.         if ( c == '\0' )
  644.                 pt->revno = nil;
  645.         else
  646.                 pt->revno = sp;
  647.         }
  648.         pt->nextsym = nil;
  649.         if (lastassoc)
  650.             lastassoc->nextsym = pt;
  651.         else
  652.             assoclst = pt;
  653.         lastassoc = pt;
  654.         return;
  655. }
  656.  
  657.  
  658.  
  659. struct access * getaccessor( sp)
  660. char            *sp;
  661. /*   Function:  get the accessor list of options -e and -a,     */
  662. /*              and store in curpt                              */
  663.  
  664.  
  665. {
  666.         struct  access  * curpt, * pt,  *pre;
  667.         char    *temp;
  668.         register c;
  669.  
  670.         while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
  671.         if ( c == '\0') {
  672.             error("Missing login name after option -a or -e");
  673.             return nil;
  674.         }
  675.  
  676.         curpt = pt = nil;
  677.         while( c != '\0') {
  678.                 temp=checkid(sp,',');
  679.                 pt = (struct access *)talloc(sizeof(struct access));
  680.                 pt->login = sp;
  681.                 if ( curpt )
  682.                     pre->nextaccess = pt;
  683.                 else
  684.                     curpt = pt;
  685.                 pt->nextaccess = curpt;
  686.                 pre = pt;
  687.                 sp = temp;    c = *sp;   *sp = '\0';
  688.                 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
  689.         }
  690.         return pt;
  691. }
  692.  
  693.  
  694.  
  695. getstates(sp)
  696. char    *sp;
  697. /*   Function:  get one state attribute and the corresponding   */
  698. /*              revision and store in statelst                  */
  699.  
  700. {
  701.         char    *temp, *temp2;
  702.         struct  Status  *pt;
  703.         register        c;
  704.  
  705.         while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
  706.         temp = sp;
  707.         temp2=checkid(sp,':');  /* check for invalid state attribute */
  708.         sp = temp2;   c = *sp;   *sp = '\0';
  709.         while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
  710.  
  711.         if ( c == '\0' ) {  /*  change state of def. branch or Head  */
  712.             chgheadstate = true;
  713.             headstate  = temp;
  714.             return;
  715.         }
  716.         else if ( c != ':' ) {
  717.             error("Missing ':' after state in option -s");
  718.             return;
  719.         }
  720.  
  721.         while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
  722.         pt = (struct Status *)talloc(sizeof(struct Status));
  723.         pt->status     = temp;
  724.         pt->revno      = sp;
  725.         pt->nextstatus = nil;
  726.         if (laststate)
  727.             laststate->nextstatus = pt;
  728.         else
  729.             statelst = pt;
  730.         laststate = pt;
  731. }
  732.  
  733.  
  734.  
  735. getrplaccess()
  736. /*   Function : get the accesslist of the 'accessfile'  */
  737. /*              and place in rplaccessor                */
  738. {
  739.         register        char    *id, *nextp;
  740.         struct          access  *newaccess, *oldaccess;
  741.  
  742.         if ( (finptr=fopen(accessfile, "r")) == NULL) {
  743.             faterror("Can't open file %s", accessfile);
  744.         }
  745.         Lexinit();
  746.         nextp = &accessorlst[0];
  747.  
  748.         if ( ! getkey(Khead)) faterror("Missing head in %s", accessfile);
  749.         VOID getnum();
  750.         if ( ! getlex(SEMI) ) serror("Missing ';' after head in %s",accessfile);
  751.  
  752.         if (getkey(Kbranch)) { /* optional */
  753.                 Dbranch=getnum();
  754.                 if (!getlex(SEMI)) serror("Missing ';' after branch list");
  755.         }
  756.  
  757.  
  758. #ifdef COMPAT2
  759.         /* read suffix. Only in release 2 format */
  760.         if (getkey(Ksuffix)) {
  761.             if (nexttok==STRING) {
  762.                 readstring(); nextlex(); /*through away the suffix*/
  763.             } elsif(nexttok==ID) {
  764.                 nextlex();
  765.             }
  766.             if ( ! getlex(SEMI) ) serror("Missing ';' after suffix in %s",accessfile);
  767.         }
  768. #endif
  769.  
  770.         if (! getkey(Kaccess))fatserror("Missing access list in %s",accessfile);
  771.         oldaccess = nil;
  772.         while( id =getid() ) {
  773.             newaccess = (struct access *)talloc(sizeof(struct access));
  774.             newaccess->login = nextp;
  775.             newaccess->nextaccess = nil;
  776.             while( ( *nextp++ = *id++) != '\0')  ;
  777.             if ( oldaccess )
  778.                 oldaccess->nextaccess = newaccess;
  779.             else
  780.                 rplaccessor = newaccess;
  781.             oldaccess = newaccess;
  782.         }
  783.         if ( ! getlex(SEMI))serror("Missing ';' after access list in %s",accessfile);
  784.         return;
  785. }
  786.  
  787.  
  788.  
  789. getdelrev(sp)
  790. char    *sp;
  791. /*   Function:  get revision range or branch to be deleted,     */
  792. /*              and place in delrev                             */
  793. {
  794.         int    c;
  795.         struct  delrevpair      *pt;
  796.  
  797.         if (delrev) free((char *)delrev);
  798.  
  799.         pt = (struct delrevpair *)talloc(sizeof(struct delrevpair));
  800.         while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
  801.  
  802.         if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
  803.             while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
  804.             pt->strt = sp;    pt->code = 1;
  805.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
  806.             *sp = '\0';
  807.             pt->end = nil;  delrev = pt;
  808.             return;
  809.         }
  810.         else {
  811.             pt->strt = sp;
  812.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
  813.                    && c != '-' && c != '<' )  c = *++sp;
  814.             *sp = '\0';
  815.             while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
  816.             if ( c == '\0' )  {  /*   -o rev or branch   */
  817.                 pt->end = nil;   pt->code = 0;
  818.                 delrev = pt;
  819.                 return;
  820.             }
  821.             if ( c != '-' && c != '<') {
  822.                 faterror("Invalid range %s %s after -o", pt->strt, sp);
  823.                 free((char *)pt);
  824.                 return;
  825.             }
  826.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  827.             if ( c == '\0') {  /*  -o   rev-   or   rev<   */
  828.                 pt->end = nil;   pt->code = 2;
  829.                 delrev = pt;
  830.                 return;
  831.             }
  832.         }
  833.         /*   -o   rev1-rev2    or   rev1<rev2   */
  834.         pt->end = sp;  pt->code = 3;   delrev = pt;
  835.         while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
  836.         *sp = '\0';
  837. }
  838.  
  839.  
  840.  
  841.  
  842. scanlogtext(delta,func)
  843. struct hshentry * delta; enum stringwork func;
  844. /* Function: Scans delta text nodes up to and including the one given
  845.  * by delta, or up to last one present, if delta==nil.
  846.  * For the one given by delta (if delta!=nil), the log message is saved into
  847.  * curlogmsg and the text is processed according to parameter func.
  848.  * Assumes the initial lexeme must be read in first.
  849.  * Does not advance nexttok after it is finished, except if delta==nil.
  850.  */
  851. {       struct hshentry * nextdelta;
  852.  
  853.         do {
  854.                 rewriteflag = false;
  855.                 nextlex();
  856.                 if (!(nextdelta=getnum())) {
  857.                     if(delta)
  858.                         faterror("Can't find delta for revision %s", delta->num);
  859.                     else return; /* no more delta text nodes */
  860.                 }
  861.                 if ( nextdelta->selector != DELETE) {
  862.                         rewriteflag = true;
  863.                         VOID fprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
  864.                 }
  865.                 if (!getkey(Klog) || nexttok!=STRING)
  866.                         serror("Missing log entry");
  867.                 elsif (delta==nextdelta) {
  868.                         VOID savestring(curlogmsg,logsize);
  869.                         delta->log=curlogmsg;
  870.                 } else {readstring();
  871.                         if (delta!=nil) delta->log="";
  872.                 }
  873.                 nextlex();
  874.                 if (!getkey(Ktext) || nexttok!=STRING)
  875.                         fatserror("Missing delta text");
  876.  
  877.                 if(delta==nextdelta)
  878.                         /* got the one we're looking for */
  879.                         switch (func) {
  880.                         case copy:      copystring();
  881.                                         break;
  882.                         case edit:      editstring((struct hshentry *)nil);
  883.                                         break;
  884.                         default:        faterror("Wrong scanlogtext");
  885.                         }
  886.                 else    readstring(); /* skip over it */
  887.  
  888.         } while (delta!=nextdelta);
  889. }
  890.  
  891.  
  892.  
  893. releaselst(sourcelst)
  894. struct  access  * sourcelst;
  895. /*   Function:  release the storages whose address are in sourcelst   */
  896.  
  897. {
  898.         struct  access  * pt;
  899.  
  900.         pt = sourcelst;
  901.         while(pt) {
  902.         struct access *pn = pt->nextaccess;
  903.             free((char *)pt);
  904.             pt = pn;
  905.         }
  906. }
  907.  
  908.  
  909.  
  910. struct  Lockrev  * rmnewlocklst(which)
  911. struct  Lockrev  * which;
  912. /*   Function:  remove lock to revision which->revno from newlocklst   */
  913.  
  914. {
  915.         struct  Lockrev   * pt, *pre;
  916.  
  917.         while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
  918.         struct Lockrev *pn = newlocklst->nextrev;
  919.             free((char *)newlocklst);
  920.         newlocklst = pn;
  921.         }
  922.  
  923.         pt = pre = newlocklst;
  924.         while( pt ) {
  925.             if ( ! strcmp(pt->revno, which->revno) ) {
  926.                 pre->nextrev = pt->nextrev;
  927.                 free((char *)pt);
  928.         pt = pre->nextrev;
  929.             }
  930.             else {
  931.                 pre = pt;
  932.                 pt = pt->nextrev;
  933.             }
  934.         }
  935.         return pre;
  936. }
  937.  
  938.  
  939.  
  940. struct  access  * removeaccess( who, sourcelst,flag)
  941. struct  access  * who, * sourcelst;
  942. int     flag;
  943. /*   Function:  remove the accessor-- who from sourcelst     */
  944.  
  945. {
  946.         struct  access  *pt, *pre;
  947.  
  948.         pt = sourcelst;
  949.         while( pt && (! strcmp(who->login, pt->login) )) {
  950.         pre = pt->nextaccess;
  951.             free((char *)pt);
  952.         pt = pre;
  953.             flag = false;
  954.     }
  955.         pre = sourcelst = pt;
  956.         while( pt ) {
  957.             if ( ! strcmp(who->login, pt->login) ) {
  958.         pre->nextaccess = pt->nextaccess;
  959.         free((char *)pt);
  960.         pt = pre->nextaccess;
  961.                 flag = false;
  962.             }
  963.             else {
  964.                 pre = pt;
  965.                 pt = pt->nextaccess;
  966.             }
  967.         }
  968.         if ( flag ) warn("Can't remove a nonexisting accessor %s",who->login);
  969.         return sourcelst;
  970. }
  971.  
  972.  
  973.  
  974. int addnewaccess( who )
  975. struct  access  * who;
  976. /*   Function:  add new accessor-- who into AccessList    */
  977.  
  978. {
  979.         struct  access  *pt,  *pre;
  980.  
  981.         pre = pt = AccessList;
  982.  
  983.         while( pt ) {
  984.             if ( strcmp( who->login, pt->login) ) {
  985.                 pre = pt;
  986.                 pt = pt->nextaccess;
  987.             }
  988.             else
  989.                 return 0;
  990.         }
  991.         if ( pre == pt )
  992.             AccessList = who;
  993.         else
  994.             pre->nextaccess = who;
  995.         return 1;
  996. }
  997.  
  998.  
  999. sendmail(Delta, who)
  1000. char    * Delta,  *who;
  1001. /*   Function:  mail to who, informing him that his lock on delta was
  1002.  *   broken by caller. Ask first whether to go ahead. Return false on
  1003.  *   error or if user decides not to break the lock.
  1004.  */
  1005. {
  1006.         char    * messagefile;
  1007.         int   old1, old2, c, response;
  1008.         FILE    * mailmess;
  1009.  
  1010.  
  1011.     VOID fprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
  1012.         VOID fprintf(stderr, "Do you want to break the lock? [ny](n): ");
  1013.         response=c=getchar();
  1014.         while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
  1015.         if (response=='\n'||response=='n'||response=='N') return false;
  1016.  
  1017.         /* go ahead with breaking  */
  1018.         messagefile=mktempfile("/tmp/", "RCSmailXXXXXX");
  1019.         if ( (mailmess = fopen(messagefile, "w")) == NULL) {
  1020.             faterror("Can't open file %s", messagefile);
  1021.         }
  1022.  
  1023.     VOID fprintf(mailmess, "Subject: Broken lock on %s\n\n",bindex(RCSfilename,'/'));
  1024.         VOID fprintf(mailmess, "Your lock on revision %s of file %s\n",Delta, getfullRCSname());
  1025.         VOID fprintf(mailmess,"has been broken by %s for the following reason:\n",caller);
  1026.         VOID fputs("State the reason for breaking the lock:\n", stderr);
  1027.         VOID fputs("(terminate with ^D or single '.')\n>> ", stderr);
  1028.  
  1029.         old1 = '\n';    old2 = ' ';
  1030.         for (; ;) {
  1031.             c = getchar();
  1032.             if ( c == EOF ) {
  1033.                 VOID putc('\n',stderr);
  1034.                 VOID fprintf(mailmess, "%c\n", old1);
  1035.                 break;
  1036.             }
  1037.             else if ( c == '\n' && old1 == '.' && old2 == '\n')
  1038.                 break;
  1039.             else {
  1040.                 VOID fputc( old1, mailmess);
  1041.                 old2 = old1;   old1 = c;
  1042.                 if (c== '\n') VOID fputs(">> ", stderr);
  1043.             }
  1044.         }
  1045.         ffclose(mailmess);
  1046.  
  1047.     /* ignore the exit status, even if delivermail unsuccessful */
  1048.         VOID run(messagefile,(char*)nil,
  1049. #ifdef SENDMAIL
  1050.         "/usr/lib/sendmail",
  1051. #else
  1052. #  ifdef DELIVERMAIL
  1053.         "/etc/delivermail","-w",
  1054. #  else
  1055.         "/bin/mail",
  1056. #  endif
  1057. #endif
  1058.         who,(char*)nil);
  1059.         VOID unlink(messagefile);
  1060.     return(true);
  1061. }
  1062.  
  1063.  
  1064.  
  1065. static breaklock(who,delta)
  1066. char * who; struct hshentry * delta;
  1067. /* function: Finds the lock held by who on delta,
  1068.  * and removes it.
  1069.  * Sends mail if a lock different from the caller's is broken.
  1070.  * Prints an error message if there is no such lock or error.
  1071.  */
  1072. {
  1073.         register struct lock * next, * trail;
  1074.         char * num;
  1075.         struct lock dummy;
  1076.         int whor, numr;
  1077.  
  1078.     num=delta->num;
  1079.         dummy.nextlock=next=Locks;
  1080.         trail = &dummy;
  1081.         while (next!=nil) {
  1082.         if (num != nil)
  1083.             numr = strcmp(num, next->delta->num);
  1084.  
  1085.         whor=strcmp(who,next->login);
  1086.         if (whor==0 && numr==0) break; /* exact match */
  1087.         if (numr==0 && whor !=0) {
  1088.                         if (!sendmail( num, next->login)){
  1089.                             diagnose("%s still locked by %s",num,next->login);
  1090.                 return;
  1091.                         } else break; /* continue after loop */
  1092.                 }
  1093.                 trail=next;
  1094.                 next=next->nextlock;
  1095.         }
  1096.         if (next!=nil) {
  1097.                 /*found one */
  1098.                 diagnose("%s unlocked",next->delta->num);
  1099.                 trail->nextlock=next->nextlock;
  1100.                 next->delta->lockedby=nil;
  1101.                 Locks=dummy.nextlock;
  1102.         } else  {
  1103.         error("no lock set on revision %s", num);
  1104.         }
  1105. }
  1106.  
  1107.  
  1108.  
  1109. struct hshentry *searchcutpt(object, length, store)
  1110. char    * object;
  1111. int     length;
  1112. struct  hshentry   * * store;
  1113. /*   Function:  Search store and return entry with number being object. */
  1114. /*              cuttail = nil, if the entry is Head; otherwise, cuttail */
  1115. /*              is the entry point to the one with number being object  */
  1116.  
  1117. {
  1118.         while( compartial( (*store++)->num, object, length)  )  ;
  1119.         store--;
  1120.  
  1121.         if ( *store == Head)
  1122.             cuthead = nil;
  1123.         else
  1124.             cuthead = *(store -1);
  1125.         return *store;
  1126. }
  1127.  
  1128.  
  1129.  
  1130. int  branchpoint(strt, tail)
  1131. struct  hshentry        *strt,  *tail;
  1132. /*   Function: check whether the deltas between strt and tail    */
  1133. /*        are locked or branch point, return 1 if any is  */
  1134. /*        locked or branch point; otherwise, return 0 and */
  1135. /*              mark DELETE on selector                         */
  1136.  
  1137. {
  1138.         struct  hshentry    *pt;
  1139.     struct lock  *lockpt;
  1140.         int     flag;
  1141.  
  1142.  
  1143.         pt = strt;
  1144.         flag = false;
  1145.         while( pt != tail) {
  1146.             if ( pt->branches ){ /*  a branch point  */
  1147.                 flag = true;
  1148.                 error("Can't remove branch point %s", pt->num);
  1149.             }
  1150.         lockpt = Locks;
  1151.         while(lockpt && lockpt->delta != pt)
  1152.         lockpt = lockpt->nextlock;
  1153.         if ( lockpt ) {
  1154.         flag = true;
  1155.         error("Can't remove locked revision %s",pt->num);
  1156.         }
  1157.             pt = pt->next;
  1158.         }
  1159.  
  1160.         if ( ! flag ) {
  1161.             pt = strt;
  1162.             while( pt != tail ) {
  1163.                 pt->selector = DELETE;
  1164.                 diagnose("deleting revision %s ",pt->num);
  1165.                 pt = pt->next;
  1166.             }
  1167.         }
  1168.         return flag;
  1169. }
  1170.  
  1171.  
  1172.  
  1173. removerevs()
  1174. /*   Function:  get the revision range to be removed, and place the     */
  1175. /*              first revision removed in delstrt, the revision before  */
  1176. /*              delstrt in cuthead( nil, if delstrt is head), and the   */
  1177. /*              revision after the last removed revision in cuttail(nil */
  1178. /*              if the last is a leaf                                   */
  1179.  
  1180. {
  1181.         struct  hshentry    *target, *target2, * temp, *searchcutpt();
  1182.         int     length, flag;
  1183.  
  1184.         flag = false;
  1185.         if ( ! expandsym(delrev->strt, &numrev[0]) ) return 0;
  1186.         target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1187.         if ( ! target ) return 0;
  1188.         if ( cmpnum(target->num, &numrev[0]) ) flag = true;
  1189.         length = countnumflds( &numrev[0] );
  1190.  
  1191.         if ( delrev->code == 0 ) {  /*  -o  rev    or    -o  branch   */
  1192.         if ( length % 2)
  1193.         temp=searchcutpt(target->num,length+1,gendeltas);
  1194.         else if (flag) {
  1195.                 error("Revision %s does not exist", &numrev[0]);
  1196.         return 0;
  1197.         }
  1198.         else
  1199.         temp = searchcutpt(&numrev[0],length,gendeltas);
  1200.         cuttail = target->next;
  1201.             if ( branchpoint(temp, cuttail) ) {
  1202.                 cuttail = nil;
  1203.                 return 0;
  1204.             }
  1205.             delstrt = temp;     /* first revision to be removed   */
  1206.             return 1;
  1207.         }
  1208.  
  1209.         if ( length % 2 ) {   /*  invalid branch after -o   */
  1210.             error("Invalid branch range %s after -o", &numrev[0]);
  1211.             return 0;
  1212.         }
  1213.  
  1214.         if ( delrev->code == 1 )  {  /*  -o  -rev   */
  1215.             if ( length > 2 ) {
  1216.                 temp = searchcutpt( target->num, length-1, gendeltas);
  1217.                 cuttail = target->next;
  1218.             }
  1219.             else {
  1220.                 temp = searchcutpt(target->num, length, gendeltas);
  1221.                 cuttail = target;
  1222.                 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
  1223.                     cuttail = cuttail->next;
  1224.             }
  1225.             if ( branchpoint(temp, cuttail) ){
  1226.                 cuttail = nil;
  1227.                 return 0;
  1228.             }
  1229.             delstrt = temp;
  1230.             return 1;
  1231.         }
  1232.  
  1233.         if ( delrev->code == 2 )  {   /*  -o  rev-   */
  1234.             if ( length == 2 ) {
  1235.                 temp = searchcutpt(target->num, 1,gendeltas);
  1236.                 if ( flag)
  1237.                     cuttail = target;
  1238.                 else
  1239.                     cuttail = target->next;
  1240.             }
  1241.             else  {
  1242.                 if ( flag){
  1243.                     cuthead = target;
  1244.                     if ( !(temp = target->next) ) return 0;
  1245.                 }
  1246.                 else
  1247.                     temp = searchcutpt(target->num, length, gendeltas);
  1248.                 getbranchno(temp->num, &numrev[0]);  /*  get branch number  */
  1249.                 target = genrevs(&numrev[0], (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1250.             }
  1251.             if ( branchpoint( temp, cuttail ) ) {
  1252.                 cuttail = nil;
  1253.                 return 0;
  1254.             }
  1255.             delstrt = temp;
  1256.             return 1;
  1257.         }
  1258.  
  1259.         /*   -o   rev1-rev2   */
  1260.         if ( ! expandsym(delrev->end, &numrev[0])  )  return 0;
  1261.         if ( length != countnumflds( &numrev[0] ) ) {
  1262.             error("Invalid revision range %s-%s", target->num, &numrev[0]);
  1263.             return 0;
  1264.         }
  1265.         if ( length > 2 && compartial( &numrev[0], target->num, length-1) ) {
  1266.             error("Invalid revision range %s-%s", target->num, &numrev[0]);
  1267.             return 0;
  1268.         }
  1269.  
  1270.         target2 = genrevs( &numrev[0], (char *)nil, (char *)nil, (char *)nil,gendeltas);
  1271.         if ( ! target2 ) return 0;
  1272.  
  1273.         if ( length > 2) {  /* delete revisions on branches  */
  1274.             if ( cmpnum(target->num, target2->num) > 0) {
  1275.                 if ( cmpnum(target2->num, &numrev[0]) )
  1276.                     flag = true;
  1277.                 else
  1278.                     flag = false;
  1279.                 temp = target;
  1280.                 target = target2;
  1281.                 target2 = temp;
  1282.             }
  1283.             if ( flag ) {
  1284.                 if ( ! cmpnum(target->num, target2->num) ) {
  1285.                     error("Revisions %s-%s don't exist", delrev->strt,delrev->end);
  1286.                     return 0;
  1287.                 }
  1288.                 cuthead = target;
  1289.                 temp = target->next;
  1290.             }
  1291.             else
  1292.                 temp = searchcutpt(target->num, length, gendeltas);
  1293.             cuttail = target2->next;
  1294.         }
  1295.         else { /*  delete revisions on trunk  */
  1296.             if ( cmpnum( target->num, target2->num) < 0 ) {
  1297.                 temp = target;
  1298.                 target = target2;
  1299.                 target2 = temp;
  1300.             }
  1301.             else
  1302.                 if ( cmpnum(target2->num, &numrev[0]) )
  1303.                     flag = true;
  1304.                 else
  1305.                     flag = false;
  1306.             if ( flag ) {
  1307.                 if ( ! cmpnum(target->num, target2->num) ) {
  1308.                     error("Revisions %s-%s don't exist", delrev->strt, delrev->end);
  1309.                     return 0;
  1310.                 }
  1311.                 cuttail = target2;
  1312.             }
  1313.             else
  1314.                 cuttail = target2->next;
  1315.             temp = searchcutpt(target->num, length, gendeltas);
  1316.         }
  1317.         if ( branchpoint(temp, cuttail) )  {
  1318.             cuttail = nil;
  1319.             return 0;
  1320.         }
  1321.         delstrt = temp;
  1322.         return 1;
  1323. }
  1324.  
  1325.  
  1326.  
  1327. updateassoc()
  1328. /*   Function: add or delete(if revno is nil) association    */
  1329. /*        which is stored in assoclst            */
  1330.  
  1331. {
  1332.         struct  Symrev  * curassoc;
  1333.     struct  assoc   * pre,  * pt;
  1334.         struct  hshentry    * target;
  1335.  
  1336.         /*  add new associations   */
  1337.         curassoc = assoclst;
  1338.         while( curassoc ) {
  1339.             if ( curassoc->revno == nil ) {  /* delete symbol  */
  1340.         pre = pt = Symbols;
  1341.                 while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
  1342.             pre = pt;
  1343.             pt = pt->nextassoc;
  1344.         }
  1345.         if ( pt )
  1346.             if ( pre == pt )
  1347.             Symbols = pt->nextassoc;
  1348.             else
  1349.             pre->nextassoc = pt->nextassoc;
  1350.         else
  1351.                     warn("Can't delete nonexisting symbol %s",curassoc->ssymbol);
  1352.         }
  1353.             else if ( expandsym( curassoc->revno, &numrev[0] ) ) {
  1354.         /*   add symbol  */
  1355.                target = (struct hshentry *) talloc(sizeof(struct hshentry));
  1356.                target->num = &numrev[0];
  1357.                VOID addsymbol(target, curassoc->ssymbol, curassoc->override);
  1358.             }
  1359.             curassoc = curassoc->nextsym;
  1360.         }
  1361.  
  1362. }
  1363.  
  1364.  
  1365.  
  1366. updatelocks()
  1367. /* Function: remove lock for caller or first lock if unlockcaller==true;
  1368.  *           remove locks which are stored in rmvlocklst,
  1369.  *           add new locks which are stored in newlocklst,
  1370.  *           add lock for Dbranch or Head if lockhead==true.
  1371.  */
  1372. {
  1373.         struct  hshentry        *target;
  1374.         struct  Lockrev         *lockpt;
  1375.  
  1376.         if(unlockcaller == true) { /*  find lock for caller  */
  1377.             if ( Head ) {
  1378.         if (Locks) {
  1379.             target=findlock(caller,true);
  1380.             if (target==nil) {
  1381.             breaklock(caller, Locks->delta); /* remove most recent lock */
  1382.             } else {
  1383.             diagnose("%s unlocked",target->num);
  1384.             }
  1385.         } else {
  1386.             warn("There are no locks set.");
  1387.         }
  1388.             } else {
  1389.                 warn("Can't unlock an empty tree");
  1390.             }
  1391.         }
  1392.  
  1393.         /*  remove locks which are stored in rmvlocklst   */
  1394.         lockpt = rmvlocklst;
  1395.         while( lockpt ) {
  1396.         if (expandsym(lockpt->revno, numrev)) {
  1397.         target = genrevs(numrev, (char *)nil, (char *)nil, (char *)nil, gendeltas);
  1398.                 if ( target )
  1399.            if ( !(countnumflds(numrev)%2) && cmpnum(target->num,numrev))
  1400.             error("Can't unlock nonexisting revision %s",lockpt->revno);
  1401.                    else
  1402.                         breaklock(caller, target);
  1403.                         /* breaklock does its own diagnose */
  1404.             }
  1405.             lockpt = lockpt->nextrev;
  1406.         }
  1407.  
  1408.         /*  add new locks which stored in newlocklst  */
  1409.         lockpt = newlocklst;
  1410.         while( lockpt ) {
  1411.             setlock(lockpt->revno,caller);
  1412.             lockpt = lockpt->nextrev;
  1413.         }
  1414.  
  1415.         if ( lockhead == true) {  /*  lock default branch or head  */
  1416.             if (Dbranch) {
  1417.                 setlock(Dbranch->num,caller);
  1418.             } elsif ( Head) {
  1419.                 if (addlock(Head, caller))
  1420.                     diagnose("%s locked",Head->num);
  1421.             } else {
  1422.                 warn("Can't lock an empty tree");
  1423.             }
  1424.         }
  1425.  
  1426. }
  1427.  
  1428.  
  1429.  
  1430. setlock(rev,who)
  1431. char * rev, * who;
  1432. /* Function: Given a revision or branch number, finds the correponding
  1433.  * delta and locks it for who.
  1434.  */
  1435. {
  1436.         struct  lock     *lpt;
  1437.         struct  hshentry *target;
  1438.  
  1439.         if (expandsym(rev, &numrev[0]) ){
  1440.             target = genrevs(&numrev[0],(char *) nil,(char *) nil,
  1441.                  (char *)nil, gendeltas);
  1442.             if ( target )
  1443.                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num,&numrev[0]))
  1444.                     error("Can't lock nonexisting revision %s",numrev);
  1445.                else
  1446.                     if(lpt=addlock(target, who))
  1447.                         diagnose("%s locked",lpt->delta->num);
  1448.         }
  1449. }
  1450.  
  1451.  
  1452.  
  1453. rcs_setstate(rev,status)
  1454. char * rev, * status;
  1455. /* Function: Given a revision or branch number, finds the corresponding delta
  1456.  * and sets its state to status.
  1457.  */
  1458. {
  1459.         struct  hshentry *target;
  1460.  
  1461.         if ( expandsym(rev, &numrev[0]) ) {
  1462.             target = genrevs(&numrev[0],(char *) nil, (char *)nil,
  1463.                  (char *) nil, gendeltas);
  1464.             if ( target )
  1465.                if ( !(countnumflds(&numrev[0])%2) && cmpnum(target->num, &numrev[0]) )
  1466.                     error("Can't set state of nonexisting revision %s to %s",
  1467.                            numrev,status);
  1468.                else
  1469.                     target->state = status;
  1470.         }
  1471. }
  1472.  
  1473.  
  1474.  
  1475.  
  1476.  
  1477. buildeltatext(deltas)
  1478. struct  hshentry        ** deltas;
  1479. /*   Function:  put the delta text on frewrite and make necessary   */
  1480. /*              change to delta text                                */
  1481. {
  1482.         int  i, c, exit_stats;
  1483.  
  1484.         cuttail->selector = DELETE;
  1485.         initeditfiles("/tmp/");
  1486.         scanlogtext(deltas[0], copy);
  1487.         i = 1;
  1488.         if ( cuthead )  {
  1489.             cutfilename=mktempfile("/tmp/", "RCScutXXXXXX");
  1490.             if ( (fcut = fopen(cutfilename, "w")) == NULL) {
  1491.                 faterror("Can't open temporary file %s", cutfilename);
  1492.             }
  1493.  
  1494.             while( deltas[i-1] != cuthead )  {
  1495.                 scanlogtext(deltas[i++], edit);
  1496.             }
  1497.  
  1498.             finishedit((struct hshentry *)nil);    rewind(fcopy);
  1499.             while( (c = getc(fcopy)) != EOF) VOID putc(c, fcut);
  1500.             swapeditfiles(false);
  1501.             ffclose(fcut);
  1502.         }
  1503.  
  1504.         while( deltas[i-1] != cuttail)
  1505.             scanlogtext(deltas[i++], edit);
  1506.         finishedit((struct hshentry *)nil);    ffclose(fcopy);
  1507.  
  1508.         if ( cuthead ) {
  1509.             diffilename=mktempfile("/tmp/", "RCSdifXXXXXX");
  1510.             exit_stats = run((char*)nil,diffilename,
  1511.             DIFF,"-n",cutfilename,resultfile,(char*)nil);
  1512.             if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  1513.                 faterror ("diff failed");
  1514.             if(!putdtext(cuttail->num,curlogmsg,diffilename,frewrite)) return;
  1515.         }
  1516.         else
  1517.             if (!putdtext(cuttail->num,curlogmsg,resultfile,frewrite)) return;
  1518.  
  1519.         scanlogtext((struct hshentry *)nil,empty); /* read the rest of the deltas */
  1520. }
  1521.  
  1522.  
  1523.  
  1524. buildtree()
  1525. /*   Function:  actually removes revisions whose selector field  */
  1526. /*              is DELETE, and rebuilds  the linkage of deltas.  */
  1527. /*              asks for reconfirmation if deleting last revision*/
  1528. {
  1529.     int c,  response;
  1530.  
  1531.     struct    hshentry   * Delta;
  1532.         struct  branchhead      *pt, *pre;
  1533.  
  1534.         if ( cuthead )
  1535.            if ( cuthead->next == delstrt )
  1536.                 cuthead->next = cuttail;
  1537.            else {
  1538.                 pre = pt = cuthead->branches;
  1539.                 while( pt && pt->hsh != delstrt )  {
  1540.                     pre = pt;
  1541.                     pt = pt->nextbranch;
  1542.                 }
  1543.                 if ( cuttail )
  1544.                     pt->hsh = cuttail;
  1545.                 else if ( pt == pre )
  1546.                     cuthead->branches = pt->nextbranch;
  1547.                 else
  1548.                     pre->nextbranch = pt->nextbranch;
  1549.             }
  1550.     else {
  1551.             if ( cuttail == nil && !quietflag) {
  1552.                 VOID fprintf(stderr,"Do you really want to delete all revisions ?[ny](n): ");
  1553.         c = response = getchar();
  1554.         while( c != EOF && c != '\n') c = getchar();
  1555.                 if ( response != 'y' && response != 'Y') {
  1556.                     diagnose("No revision deleted");
  1557.             Delta = delstrt;
  1558.             while( Delta) {
  1559.             Delta->selector = 'S';
  1560.             Delta = Delta->next;
  1561.             }
  1562.             return;
  1563.         }
  1564.         }
  1565.             Head = cuttail;
  1566.     }
  1567.         return;
  1568. }
  1569.  
  1570.