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