home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / RCS_SRC.ZIP / CO.C < prev    next >
C/C++ Source or Header  |  1991-01-15  |  25KB  |  638 lines

  1. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2.    Distributed under license by the Free Software Foundation, Inc.
  3.  
  4. This file is part of RCS.
  5.  
  6. RCS is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. RCS is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with RCS; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. Report problems and direct all questions to:
  21.  
  22.     rcs-bugs@cs.purdue.edu
  23.  
  24. */
  25.  
  26. /*
  27.  *                     RCS checkout operation
  28.  */
  29. #ifndef lint
  30. static char rcsid[]=
  31. "$Header: /usr/src/local/bin/rcs/src/RCS/co.c,v 4.7 89/05/01 15:11:41 narten Exp $ Purdue CS";
  32. #endif
  33. /*****************************************************************************
  34.  *                       check out revisions from RCS files
  35.  *****************************************************************************
  36.  */
  37.  
  38.  
  39. /* $Log:    co.c,v $
  40.  * Revision 4.7  89/05/01  15:11:41  narten
  41.  * changed copyright header to reflect current distribution rules
  42.  * 
  43.  * Revision 4.6  88/11/08  12:02:31  narten
  44.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  45.  * 
  46.  * Revision 4.6  88/08/09  19:12:15  eggert
  47.  * Fix "co -d" core dump; rawdate wasn't always initialized.
  48.  * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
  49.  * 
  50.  * Revision 4.5  87/12/18  11:35:40  narten
  51.  * lint cleanups (from Guy Harris)
  52.  * 
  53.  * Revision 4.4  87/10/18  10:20:53  narten
  54.  * Updating version numbers changes relative to 1.1, are actually
  55.  * relative to 4.2
  56.  * 
  57.  * Revision 1.3  87/09/24  13:58:30  narten
  58.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  59.  * warnings)
  60.  * 
  61.  * Revision 1.2  87/03/27  14:21:38  jenkins
  62.  * Port to suns
  63.  * 
  64.  * Revision 1.1  84/01/23  14:49:58  kcs
  65.  * Initial revision
  66.  * 
  67.  * Revision 4.2  83/12/05  13:39:48  wft
  68.  * made rewriteflag external.
  69.  * 
  70.  * Revision 4.1  83/05/10  16:52:55  wft
  71.  * Added option -u and -f.
  72.  * Added handling of default branch.
  73.  * Replaced getpwuid() with getcaller().
  74.  * Removed calls to stat(); now done by pairfilenames().
  75.  * Changed and renamed rmoldfile() to rmworkfile().
  76.  * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
  77.  * 
  78.  * Revision 3.7  83/02/15  15:27:07  wft
  79.  * Added call to fastcopy() to copy remainder of RCS file.
  80.  *
  81.  * Revision 3.6  83/01/15  14:37:50  wft
  82.  * Added ignoring of interrupts while RCS file is renamed; this avoids
  83.  * deletion of RCS files during the unlink/link window.
  84.  *
  85.  * Revision 3.5  82/12/08  21:40:11  wft
  86.  * changed processing of -d to use DATEFORM; removed actual from
  87.  * call to preparejoin; re-fixed printing of done at the end.
  88.  *
  89.  * Revision 3.4  82/12/04  18:40:00  wft
  90.  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
  91.  * Fixed printing of "done".
  92.  *
  93.  * Revision 3.3  82/11/28  22:23:11  wft
  94.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  95.  * %02d with %.2d, mode generation for working file with WORKMODE.
  96.  * Fixed nil printing. Fixed -j combined with -l and -p, and exit
  97.  * for non-existing revisions in preparejoin().
  98.  *
  99.  * Revision 3.2  82/10/18  20:47:21  wft
  100.  * Mode of working file is now maintained even for co -l, but write permission
  101.  * is removed.
  102.  * The working file inherits its mode from the RCS file, plus write permission
  103.  * for the owner. The write permission is not given if locking is strict and
  104.  * co does not lock.
  105.  * An existing working file without write permission is deleted automatically.
  106.  * Otherwise, co asks (empty answer: abort co).
  107.  * Call to getfullRCSname() added, check for write error added, call
  108.  * for getlogin() fixed.
  109.  *
  110.  * Revision 3.1  82/10/13  16:01:30  wft
  111.  * fixed type of variables receiving from getc() (char -> int).
  112.  * removed unused variables.
  113.  */
  114.  
  115.  
  116.  
  117.  
  118. #include "rcsbase.h"
  119. #include "time.h"
  120. #include <sys/types.h>
  121. #include <sys/stat.h>
  122.  
  123. #ifndef lint
  124. static char rcsbaseid[] = RCSBASE;
  125. #endif
  126. static char co[] = CO;
  127. static char merge[] = MERGE;
  128.  
  129. extern FILE * fopen();
  130. extern int    rename();
  131. extern char * getcaller();          /*get login of caller                   */
  132. extern struct hshentry * genrevs(); /*generate delta numbers                */
  133. extern char * getancestor();
  134. extern int  nextc;                  /*next input character                  */
  135. extern int  nerror;                 /*counter for errors                    */
  136. extern char Kdesc[];            /*keyword for description            */
  137. extern char * buildrevision();      /*constructs desired revision           */
  138. extern int    buildjoin();          /*join several revisions                */
  139. extern char * mktempfile();         /*temporary file name generator         */
  140. extern struct hshentry * findlock();/*find (and delete) a lock              */
  141. extern struct lock * addlock();     /*add a new lock                        */
  142. extern long   maketime();           /*convert parsed time to unix time.     */
  143. extern struct tm * localtime();     /*convert unixtime into a tm-structure  */
  144. extern FILE * finptr;               /* RCS input file                       */
  145. extern FILE * frewrite;             /* new RCS file                         */
  146. extern int    rewriteflag;          /* indicates whether input should be    */
  147.                     /* echoed to frewrite                   */
  148.  
  149. char * newRCSfilename, * neworkfilename;
  150. char * RCSfilename, * workfilename;
  151. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  152. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  153.  
  154. char * date, * rev, * state, * author, * join;
  155. char finaldate[datelength];
  156.  
  157. int forceflag, lockflag, unlockflag, tostdout;
  158. char * caller;                        /* caller's login;                    */
  159. extern quietflag;
  160.  
  161. char numericrev[revlength];           /* holds expanded revision number     */
  162. struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated      */
  163. struct hshentry * targetdelta;        /* final delta to be generated        */
  164.  
  165. char * joinlist[joinlength];          /* pointers to revisions to be joined */
  166. int lastjoin;                         /* index of last element in joinlist  */
  167.  
  168. main (argc, argv)
  169. int argc;
  170. char * argv[];
  171. {
  172.         int killock;                  /* indicates whether a lock is removed*/
  173.         char * cmdusage;
  174.         struct tm parseddate, *ftm;
  175.         char * rawdate;
  176.         long unixtime;
  177.  
  178.     catchints();
  179.         cmdid = "co";
  180.     cmdusage = "command format:\nco -f[rev] -l[rev] -p[rev] -q[rev] -r[rev] -ddate -sstate -w[login] -jjoinlist file ...";
  181.         date = rev = state = author = join = nil;
  182.     forceflag = lockflag = unlockflag = tostdout = quietflag = false;
  183.     caller=getcaller();
  184.     rawdate = "";
  185.  
  186.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  187.                 switch ((*argv)[1]) {
  188.  
  189.                 case 'r':
  190.                 revno:  if ((*argv)[2]!='\0') {
  191.                                 if (rev!=nil) warn("Redefinition of revision number");
  192.                                 rev = (*argv)+2;
  193.                         }
  194.                         break;
  195.  
  196.         case 'f':
  197.             forceflag=true;
  198.             goto revno;
  199.  
  200.                 case 'l':
  201.                         lockflag=true;
  202.                         if (unlockflag) {
  203.                                 warn("-l has precedence over -u");
  204.                                 unlockflag=false;
  205.                         }
  206.                         goto revno;
  207.  
  208.                 case 'u':
  209.                         unlockflag=true;
  210.                         if (lockflag) {
  211.                                 warn("-l has precedence over -u");
  212.                                 unlockflag=false;
  213.                         }
  214.                         goto revno;
  215.  
  216.                 case 'p':
  217.                         tostdout=true;
  218.                         goto revno;
  219.  
  220.                 case 'q':
  221.                         quietflag=true;
  222.                         goto revno;
  223.  
  224.                 case 'd':
  225.                         if ((*argv)[2]!='\0') {
  226.                                 if (date!=nil) warn("Redefinition of -d option");
  227.                                 rawdate=(*argv)+2;
  228.                         }
  229.                         /* process date/time */
  230.                         if (partime(rawdate,&parseddate)==0)
  231.                                 faterror("Can't parse date/time: %s",rawdate);
  232.                         if ((unixtime=maketime(&parseddate))== 0L)
  233.                                 faterror("Inconsistent date/time: %s",rawdate);
  234.                         ftm=localtime(&unixtime);
  235.                         VOID sprintf(finaldate,DATEFORM,
  236.                         ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  237.                         date=finaldate;
  238.                         break;
  239.  
  240.                 case 'j':
  241.                         if ((*argv)[2]!='\0'){
  242.                                 if (join!=nil)warn("Redefinition of -j option");
  243.                                 join = (*argv)+2;
  244.                         }
  245.                         break;
  246.  
  247.                 case 's':
  248.                         if ((*argv)[2]!='\0'){
  249.                                 if (state!=nil)warn("Redefinition of -s option");
  250.                                 state = (*argv)+2;
  251.                         }
  252.                         break;
  253.  
  254.                 case 'w':
  255.                         if (author!=nil)warn("Redefinition of -w option");
  256.                         if ((*argv)[2]!='\0')
  257.                                 author = (*argv)+2;
  258.                         else    author = caller;
  259.                         break;
  260.  
  261.                 default:
  262.                         faterror("unknown option: %s\n%s", *argv,cmdusage);
  263.  
  264.                 };
  265.         } /* end of option processing */
  266.  
  267.         if (argc<1) faterror("No input file\n%s",cmdusage);
  268.  
  269.         /* now handle all filenames */
  270.         do {
  271.         rewriteflag=false;
  272.         finptr=frewrite=NULL;
  273.         neworkfilename=nil;
  274.  
  275.         if (!pairfilenames(argc,argv,true,tostdout)) continue;
  276.  
  277.         /* now RCSfilename contains the name of the RCS file, and finptr
  278.          * the file descriptor. If tostdout is false, workfilename contains
  279.          * the name of the working file, otherwise undefined (not nil!).
  280.          * Also, RCSstat, workstat, and haveworkstat have been set.
  281.          */
  282.         diagnose("%s  -->  %s", RCSfilename,tostdout?"stdout":workfilename);
  283.  
  284.  
  285.         if (!tostdout && !trydiraccess(workfilename)) continue; /* give up */
  286.         if ((lockflag||unlockflag) && !checkaccesslist(caller)) continue;     /* give up */
  287.         if (!trysema(RCSfilename,lockflag||unlockflag)) continue;           /* give up */
  288.  
  289.  
  290.         gettree();  /* reads in the delta tree */
  291.  
  292.         if (Head==nil) {
  293.                 /* no revisions; create empty file */
  294.                 diagnose("no revisions present; generating empty revision 0.0");
  295.                 if (!tostdout)
  296.                         if (!creatempty()) continue;
  297.                 /* Can't reserve a delta, so don't call addlock */
  298.         } else {
  299.                 if (rev!=nil) {
  300.                         /* expand symbolic revision number */
  301.                         if (!expandsym(rev,numericrev))
  302.                                 continue;
  303.         } elsif (unlockflag && (targetdelta=findlock(caller,false))!=nil) {
  304.             VOID strcpy(numericrev,targetdelta->num);
  305.                 } elsif (Dbranch!=nil) {
  306.                         VOID strcpy(numericrev,Dbranch->num);
  307.         } else  numericrev[0]='\0'; /* empty */
  308.                 /* get numbers of deltas to be generated */
  309.                 if (!(targetdelta=genrevs(numericrev,date,author,state,gendeltas)))
  310.                         continue;
  311.                 /* check reservations */
  312.                 if (lockflag && !addlock(targetdelta,caller))
  313.                         continue;
  314.  
  315.                 if (unlockflag) {
  316.                         if((killock=rmlock(caller,targetdelta))== -1)
  317.                                 continue;
  318.                 } else {
  319.                         killock=0;
  320.                 }
  321.  
  322.                 if (join && !preparejoin()) continue;
  323.  
  324.         diagnose("revision %s%s",targetdelta->num,
  325.              lockflag?" (locked)":
  326.              unlockflag?" (unlocked)":"");
  327.  
  328.                 /* remove old working file if necessary */
  329.                 if (!tostdout)
  330.                         if (!rmworkfile()) continue;
  331.  
  332.                 /* prepare for rewriting the RCS file */
  333.                 if (lockflag||(killock==1)) {
  334.                         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  335.                         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  336.                                 error("Can't open file %s",newRCSfilename);
  337.                                 continue;
  338.                         }
  339.                         putadmin(frewrite);
  340.                         puttree(Head,frewrite);
  341.                         VOID fprintf(frewrite, "\n\n%s%c",Kdesc,nextc);
  342.                         rewriteflag=true;
  343.                 }
  344.  
  345.                 /* skip description */
  346.                 getdesc(false); /* don't echo*/
  347.  
  348.                 if (!(neworkfilename=buildrevision(gendeltas,targetdelta,
  349.                       tostdout?(join!=nil?"/tmp/":(char *)nil):workfilename,true)))
  350.                                 continue;
  351.  
  352.                 if ((lockflag||killock==1)&&nerror==0) {
  353.                         /* rewrite the rest of the RCSfile */
  354.                         fastcopy(finptr,frewrite);
  355.                         ffclose(frewrite); frewrite=NULL;
  356.             ignoreints();
  357.                         if (rename(newRCSfilename,RCSfilename)<0) {
  358.                                 error("Can't rewrite %s; saved in: %s",
  359.                                 RCSfilename, newRCSfilename);
  360.                                 newRCSfilename[0]='\0'; /* avoid deletion*/
  361.                                 restoreints();
  362.                                 break;
  363.                         }
  364.                         newRCSfilename[0]='\0'; /* avoid re-deletion by cleanup()*/
  365.                         if (chmod(RCSfilename,RCSstat.st_mode & ~0222)<0)
  366.                             warn("Can't preserve mode of %s",RCSfilename);
  367.                         restoreints();
  368.                 }
  369.  
  370. #               ifdef SNOOPFILE
  371.                 logcommand("co",targetdelta,gendeltas,caller);
  372. #               endif
  373.  
  374.                 if (join) {
  375.                         rmsema(); /* kill semaphore file so other co's can proceed */
  376.             if (!buildjoin(neworkfilename)) continue;
  377.                 }
  378.                 if (!tostdout) {
  379.             if (rename(neworkfilename,workfilename) <0) {
  380.                                 error("Can't create %s; see %s",workfilename,neworkfilename);
  381.                                 neworkfilename[0]= '\0'; /*avoid deletion*/
  382.                                 continue;
  383.                         }
  384.             neworkfilename[0]= '\0'; /*avoid re-deletion by cleanup()*/
  385.         }
  386.         }
  387.     if (!tostdout)
  388.             if (chmod(workfilename, WORKMODE(RCSstat.st_mode))<0)
  389.                 warn("Can't adjust mode of %s",workfilename);
  390.  
  391.  
  392.         if (!tostdout) diagnose("done");
  393.         } while (cleanup(),
  394.                  ++argv, --argc >=1);
  395.  
  396.         exit(nerror!=0);
  397.  
  398. }       /* end of main (co) */
  399.  
  400.  
  401. /*****************************************************************
  402.  * The following routines are auxiliary routines
  403.  *****************************************************************/
  404.  
  405. int rmworkfile()
  406. /* Function: unlinks workfilename, if it exists, under the following conditions:
  407.  * If it is read-only, workfilename is unlinked.
  408.  * Otherwise (file writable):
  409.  *   if !quietmode asks the user whether to really delete it (default: fail);
  410.  *   otherwise failure.
  411.  * Returns false on failure to unlink, true otherwise.
  412.  */
  413. {
  414.         int response, c;    /* holds user response to queries */
  415.  
  416.         if (haveworkstat< 0)      /* File doesn't exist; set by pairfilenames*/
  417.             return (true);        /* No problem */
  418.  
  419.     if ((workstat.st_mode & 0222)&&!forceflag) {    /* File is writable */
  420.             if (!quietflag) {
  421.                 VOID fprintf(stderr,"writable %s exists; overwrite? [ny](n): ",workfilename);
  422.                 /* must be stderr in case of IO redirect */
  423.                 c=response=getchar();
  424.                 while (!(c==EOF || c=='\n')) c=getchar(); /*skip rest*/
  425.                 if (!(response=='y'||response=='Y')) {
  426.                         warn("checkout aborted.");
  427.                         return false;
  428.                 }
  429.             } else {
  430.                 error("writable %s exists; checkout aborted.",workfilename);
  431.                 return false;
  432.             }
  433.         }
  434.     /* now unlink: either not writable, forceflag, or permission given */
  435.         if (unlink(workfilename) != 0) {            /* Remove failed   */
  436.             error("Can't unlink %s",workfilename);
  437.             return false;
  438.         }
  439.         return true;
  440. }
  441.  
  442.  
  443. creatempty()
  444. /* Function: creates an empty working file.
  445.  * First, removes an existing working file with rmworkfile().
  446.  */
  447. {
  448.         int  fdesc;              /* file descriptor */
  449.  
  450.         if (!rmworkfile()) return false;
  451.         fdesc=creat(workfilename,0);
  452.         if (fdesc < 0) {
  453.                 faterror("Cannot create %s",workfilename);
  454.                 return false;
  455.         } else {
  456.                 VOID close(fdesc); /* empty file */
  457.                 return true;
  458.         }
  459. }
  460.  
  461.  
  462. int rmlock(who,delta)
  463. char * who; struct hshentry * delta;
  464. /* Function: removes the lock held by who on delta.
  465.  * Returns -1 if someone else holds the lock,
  466.  * 0 if there is no lock on delta,
  467.  * and 1 if a lock was found and removed.
  468.  */
  469. {       register struct lock * next, * trail;
  470.         char * num;
  471.         struct lock dummy;
  472.         int whomatch, nummatch;
  473.  
  474.         num=delta->num;
  475.         dummy.nextlock=next=Locks;
  476.         trail = &dummy;
  477.         while (next!=nil) {
  478.                 whomatch=strcmp(who,next->login);
  479.                 nummatch=strcmp(num,next->delta->num);
  480.                 if ((whomatch==0) && (nummatch==0)) break;
  481.                      /*found a lock on delta by who*/
  482.                 if ((whomatch!=0)&&(nummatch==0)) {
  483.                     error("revision %s locked by %s; use co -r or rcs -u",num,next->login);
  484.                     return -1;
  485.                 }
  486.                 trail=next;
  487.                 next=next->nextlock;
  488.         }
  489.         if (next!=nil) {
  490.                 /*found one; delete it */
  491.                 trail->nextlock=next->nextlock;
  492.                 Locks=dummy.nextlock;
  493.                 next->delta->lockedby=nil; /* reset locked-by */
  494.                 return 1; /*success*/
  495.         } else  return 0; /*no lock on delta*/
  496. }
  497.  
  498.  
  499.  
  500.  
  501. /*****************************************************************
  502.  * The rest of the routines are for handling joins
  503.  *****************************************************************/
  504.  
  505. char * getrev(sp, tp, buffsize)
  506. register char * sp, *tp; int buffsize;
  507. /* Function: copies a symbolic revision number from sp to tp,
  508.  * appends a '\0', and returns a pointer to the character following
  509.  * the revision number; returns nil if the revision number is more than
  510.  * buffsize characters long.
  511.  * The revision number is terminated by space, tab, comma, colon,
  512.  * semicolon, newline, or '\0'.
  513.  * used for parsing the -j option.
  514.  */
  515. {
  516.         register char c;
  517.         register int length;
  518.  
  519.         length = 0;
  520.         while (((c= *sp)!=' ')&&(c!='\t')&&(c!='\n')&&(c!=':')&&(c!=',')
  521.                 &&(c!=';')&&(c!='\0')) {
  522.                 if (length>=buffsize) return false;
  523.                 *tp++= *sp++;
  524.                 length++;
  525.         }
  526.         *tp= '\0';
  527.         return sp;
  528. }
  529.  
  530.  
  531.  
  532. int preparejoin()
  533. /* Function: Parses a join list pointed to by join and places pointers to the
  534.  * revision numbers into joinlist.
  535.  */
  536. {
  537.         struct hshentry * * joindeltas;
  538.         struct hshentry * tmpdelta;
  539.         register char * j;
  540.         char symbolrev[revlength],numrev[revlength];
  541.  
  542.         joindeltas = (struct hshentry * *)talloc(hshsize*sizeof(struct hshentry *));
  543.         j=join;
  544.         lastjoin= -1;
  545.         for (;;) {
  546.                 while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
  547.                 if (*j=='\0') break;
  548.                 if (lastjoin>=joinlength-2) {
  549.                         error("too many joins");
  550.                         return(false);
  551.                 }
  552.                 if(!(j=getrev(j,symbolrev,revlength))) return false;
  553.                 if (!expandsym(symbolrev,numrev)) return false;
  554.                 tmpdelta=genrevs(numrev,(char *)nil,(char *)nil,(char *)nil,(struct hshentry * *)joindeltas);
  555.                 if (tmpdelta==nil)
  556.                         return false;
  557.                 else    joinlist[++lastjoin]=tmpdelta->num;
  558.                 while ((*j==' ') || (*j=='\t')) j++;
  559.                 if (*j == ':') {
  560.                         j++;
  561.                         while((*j==' ') || (*j=='\t')) j++;
  562.                         if (*j!='\0') {
  563.                                 if(!(j=getrev(j,symbolrev,revlength))) return false;
  564.                                 if (!expandsym(symbolrev,numrev)) return false;
  565.                                 tmpdelta=genrevs(numrev,(char *)nil,(char *)nil,(char *)nil, (struct hshentry * *) joindeltas);
  566.                                 if (tmpdelta==nil)
  567.                                         return false;
  568.                                 else    joinlist[++lastjoin]=tmpdelta->num;
  569.                         } else {
  570.                                 error("join pair incomplete");
  571.                                 return false;
  572.                         }
  573.                 } else {
  574.                         if (lastjoin==0) { /* first pair */
  575.                                 /* common ancestor missing */
  576.                                 joinlist[1]=joinlist[0];
  577.                                 lastjoin=1;
  578.                                 /*derive common ancestor*/
  579.                                 joinlist[0]=talloc(revlength);
  580.                                 if (!getancestor(targetdelta->num,joinlist[1],joinlist[0]))
  581.                                        return false;
  582.                         } else {
  583.                                 error("join pair incomplete");
  584.                                 return false;
  585.                         }
  586.                 }
  587.         }
  588.         if (lastjoin<1) {
  589.                 error("empty join");
  590.                 return false;
  591.         } else  return true;
  592. }
  593.  
  594.  
  595.  
  596. buildjoin(initialfile)
  597. char * initialfile;
  598. /* Function: merge pairs of elements in joinlist into initialfile
  599.  * If tostdout==true, copy result to stdout.
  600.  * All unlinking of initialfile, rev2, and rev3 should be done by cleanup().
  601.  */
  602. {
  603.     char commarg[revlength+3];
  604.         char subs[revlength];
  605.         char * rev2, * rev3;
  606.         int i;
  607.  
  608.         rev2=mktempfile("/tmp/",JOINFIL2);
  609.         rev3=mktempfile("/tmp/",JOINFIL3);
  610.  
  611.         i=0;
  612.         while (i<lastjoin) {
  613.                 /*prepare marker for merge*/
  614.                 if (i==0)
  615.                         VOID strcpy(subs,targetdelta->num);
  616.                 else    VOID sprintf(subs, "merge%d",i/2);
  617.                 diagnose("revision %s",joinlist[i]);
  618.                 VOID sprintf(commarg,"-p%s",joinlist[i]);
  619.                 if (run((char*)nil,rev2, co,commarg,"-q",RCSfilename,(char*)nil)) {
  620.                         nerror++;return false;
  621.                 }
  622.                 diagnose("revision %s",joinlist[i+1]);
  623.                 VOID sprintf(commarg,"-p%s",joinlist[i+1]);
  624.                 if (run((char *)nil,rev3, co,commarg,"-q",RCSfilename,(char*)nil)) {
  625.                         nerror++; return false;
  626.                 }
  627.                 diagnose("merging...");
  628.         if (
  629.                         (i+2)>=lastjoin && tostdout
  630.             ?    run((char*)nil,(char*)nil, merge,"-p",initialfile,rev2,rev3,subs,joinlist[i+1],(char*)nil)
  631.             :    run((char*)nil,(char*)nil, merge,     initialfile,rev2,rev3,subs,joinlist[i+1],(char*)nil)) {
  632.                         nerror++; return false;
  633.                 }
  634.                 i=i+2;
  635.         }
  636.         return true;
  637. }
  638.