📄 co.c
字号:
if (!rmworkfile()) continue; /* skip description */ getdesc(false); /* don't echo*/ locker_expansion = 0 < lockflag; joinfilename = buildrevision( gendeltas, targetdelta, join&&tostdout ? (FILE*)0 : neworkptr, Expand!=OLD_EXPAND );# if !large_memory if (fcopy == neworkptr) fcopy = 0; /* Don't close it twice. */# endif if_advise_access(changelock && gendeltas->first!=targetdelta, finptr, MADV_SEQUENTIAL ); if (!donerewrite(changelock)) continue; newdate = targetdelta->date; if (join) { newdate = 0; if (!joinfilename) { aflush(neworkptr); joinfilename = neworkfilename; } if (!buildjoin(joinfilename)) continue; } } if (!tostdout) { r = 0; if (mtimeflag && newdate) { if (!join) aflush(neworkptr); r = setfiledate(neworkfilename, newdate); } if (r == 0) { ignoreints(); r = chnamemod(&neworkptr, neworkfilename, workfilename, WORKMODE(RCSstat.st_mode, !(Expand==VAL_EXPAND || lockflag<=0&&StrictLocks) ) ); keepdirtemp(neworkfilename); restoreints(); } if (r != 0) { eerror(workfilename); error("see %s", neworkfilename); continue; } diagnose("done\n"); } } while (cleanup(), ++argv, --argc >=1); tempunlink(); Ofclose(workstdout); exitmain(exitstatus);} /* end of main (co) */ static voidcleanup(){ if (nerror) exitstatus = EXIT_FAILURE; Izclose(&finptr); Ozclose(&frewrite);# if !large_memory if (fcopy!=workstdout) Ozclose(&fcopy);# endif if (neworkptr!=workstdout) Ozclose(&neworkptr); dirtempunlink();}#if lint# define exiterr coExit#endif exiting voidexiterr(){ dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE);}/***************************************************************** * The following routines are auxiliary routines *****************************************************************/ static intrmworkfile()/* Function: prepares to remove workfilename, if it exists, and if * it is read-only. * Otherwise (file writable): * if !quietmode asks the user whether to really delete it (default: fail); * otherwise failure. * Returns true if permission is gotten. */{ if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { /* File is writable */ if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ", workfilename, myself(workstat.st_uid) ? "" : ", and you do not own it" )) { error(!quietflag && ttystdin() ? "checkout aborted" : "writable %s exists; checkout aborted", workfilename); return false; } } /* Actual unlink is done later by caller. */ return true;} static intrmlock(delta) struct hshentry const *delta;/* Function: removes the lock held by caller on delta. * Returns -1 if someone else holds the lock, * 0 if there is no lock on delta, * and 1 if a lock was found and removed. */{ register struct lock * next, * trail; char const *num; struct lock dummy; int whomatch, nummatch; num=delta->num; dummy.nextlock=next=Locks; trail = &dummy; while (next!=nil) { whomatch = strcmp(getcaller(), next->login); nummatch=strcmp(num,next->delta->num); if ((whomatch==0) && (nummatch==0)) break; /*found a lock on delta by caller*/ if ((whomatch!=0)&&(nummatch==0)) { error("revision %s locked by %s; use co -r or rcs -u",num,next->login); return -1; } trail=next; next=next->nextlock; } if (next!=nil) { /*found one; delete it */ trail->nextlock=next->nextlock; Locks=dummy.nextlock; next->delta->lockedby=nil; /* reset locked-by */ return 1; /*success*/ } else return 0; /*no lock on delta*/}/***************************************************************** * The rest of the routines are for handling joins *****************************************************************/ static char const *addjoin(joinrev) char *joinrev;/* Add joinrev's number to joinlist, yielding address of char past joinrev, * or nil if no such revision exists. */{ register char *j; register struct hshentry const *d; char terminator; struct buf numrev; struct hshentries *joindeltas; j = joinrev; for (;;) { switch (*j++) { default: continue; case 0: case ' ': case '\t': case '\n': case ':': case ',': case ';': break; } break; } terminator = *--j; *j = 0; bufautobegin(&numrev); d = 0; if (expandsym(joinrev, &numrev)) d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas); bufautoend(&numrev); *j = terminator; if (d) { joinlist[++lastjoin] = d->num; return j; } return nil;} static intpreparejoin()/* Function: Parses a join list pointed to by join and places pointers to the * revision numbers into joinlist. */{ register char const *j; j=join; lastjoin= -1; for (;;) { while ((*j==' ')||(*j=='\t')||(*j==',')) j++; if (*j=='\0') break; if (lastjoin>=joinlength-2) { error("too many joins"); return(false); } if (!(j = addjoin(j))) return false; while ((*j==' ') || (*j=='\t')) j++; if (*j == ':') { j++; while((*j==' ') || (*j=='\t')) j++; if (*j!='\0') { if (!(j = addjoin(j))) return false; } else { error("join pair incomplete"); return false; } } else { if (lastjoin==0) { /* first pair */ /* common ancestor missing */ joinlist[1]=joinlist[0]; lastjoin=1; /*derive common ancestor*/ if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) return false; } else { error("join pair incomplete"); return false; } } } if (lastjoin<1) { error("empty join"); return false; } else return true;} static char const *getancestor(r1, r2) char const *r1, *r2;/* Yield the common ancestor of r1 and r2 if successful, nil otherwise. * Work reliably only if r1 and r2 are not branch numbers. */{ static struct buf t1, t2; unsigned l1, l2, l3; char const *r; l1 = countnumflds(r1); l2 = countnumflds(r2); if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) { /* not on main trunk or identical */ l3 = 0; while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0) l3 += 2; /* This will terminate since r1 and r2 are not the same; see above. */ if (l3==0) { /* no common prefix; common ancestor on main trunk */ VOID partialno(&t1, r1, l1>2 ? (unsigned)2 : l1); VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2); r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) return r; } else if (cmpnumfld(r1, r2, l3+1)!=0) return partialno(&t1,r1,l3); } error("common ancestor of %s and %s undefined", r1, r2); return nil;} static intbuildjoin(initialfile) char const *initialfile;/* Function: merge pairs of elements in joinlist into initialfile * If workstdout is set, copy result to stdout. * All unlinking of initialfile, rev2, and rev3 should be done by tempunlink(). */{ struct buf commarg; struct buf subs; char const *rev2, *rev3; int i; char const *cov[10], *mergev[12]; char const **p; bufautobegin(&commarg); bufautobegin(&subs); rev2 = maketemp(0); rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ cov[0] = nil; /* cov[1] setup below */ cov[2] = CO; /* cov[3] setup below */ p = &cov[4]; if (expandarg) *p++ = expandarg; if (suffixarg) *p++ = suffixarg; if (versionarg) *p++ = versionarg; *p++ = quietarg; *p++ = RCSfilename; *p = nil; mergev[0] = nil; mergev[1] = nil; mergev[2] = MERGE; mergev[3] = mergev[5] = "-L"; /* rest of mergev setup below */ i=0; while (i<lastjoin) { /*prepare marker for merge*/ if (i==0) bufscpy(&subs, targetdelta->num); else { bufscat(&subs, ","); bufscat(&subs, joinlist[i-2]); bufscat(&subs, ":"); bufscat(&subs, joinlist[i-1]); } diagnose("revision %s\n",joinlist[i]); bufscpy(&commarg, "-p"); bufscat(&commarg, joinlist[i]); cov[1] = rev2; cov[3] = commarg.string; if (runv(cov)) goto badmerge; diagnose("revision %s\n",joinlist[i+1]); bufscpy(&commarg, "-p"); bufscat(&commarg, joinlist[i+1]); cov[1] = rev3; cov[3] = commarg.string; if (runv(cov)) goto badmerge; diagnose("merging...\n"); mergev[4] = subs.string; mergev[6] = joinlist[i+1]; p = &mergev[7]; if (quietflag) *p++ = quietarg; if (lastjoin<=i+2 && workstdout) *p++ = "-p"; *p++ = initialfile; *p++ = rev2; *p++ = rev3; *p = nil; switch (runv(mergev)) { case DIFF_FAILURE: case DIFF_SUCCESS: break; default: goto badmerge; } i=i+2; } bufautoend(&commarg); bufautoend(&subs); return true; badmerge: nerror++; bufautoend(&commarg); bufautoend(&subs); return false;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -