home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / rcs4.lha / co.c < prev    next >
Text File  |  1993-03-03  |  24KB  |  626 lines

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