Annotation of 43BSD/contrib/rcs/src/co.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.