home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / rcs4.lha / rcs.c < prev    next >
Text File  |  1993-03-27  |  53KB  |  1,558 lines

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