12150Seric # include "../hdr/defines.h" 22150Seric # include "../hdr/had.h" 32150Seric 4*19943Ssam SCCSID(@(#)rmchg.c 4.2); 52150Seric 62150Seric /* 72150Seric Program to remove a specified delta from an SCCS file, 82150Seric when invoked as 'rmdel', 92150Seric or to change the MRs and/or comments of a specified delta, 102150Seric when invoked as 'chghist'. 112150Seric (The program has two links to it, one called 'rmdel', the 122150Seric other 'chghist'.) 132150Seric 142150Seric The delta to be removed (or whose MRs and/or comments 152150Seric are to be changed) is specified via the 162150Seric r argument, in the form of an SID. 172150Seric 182150Seric If the delta is to be removed, it must be the most recent one 192150Seric in its branch in the delta tree (a so-called 'leaf' delta). 202150Seric For either function, the delta being processed must not 212150Seric have any 'delivered' MRs, and the user must have basically 222150Seric the same permissions as are required to make deltas. 232150Seric 242150Seric If a directory is given as an argument, each SCCS file 252150Seric within the directory will be processed as if it had been 262150Seric specifically named. If a name of '-' is given, the standard 272150Seric input will be read for a list of names of SCCS files to be 282150Seric processed. Non SCCS files are ignored. 292150Seric */ 302150Seric 312150Seric # define COPY 0 322150Seric # define NOCOPY 1 332150Seric 342150Seric struct sid sid; 352150Seric int num_files; 362150Seric char had[26]; 372150Seric char D_type; 382150Seric int D_serial; 392150Seric 402150Seric main(argc,argv) 412150Seric int argc; 422150Seric char *argv[]; 432150Seric { 442150Seric register int i; 452150Seric register char *p; 462150Seric char c; 472150Seric extern rmchg(); 482150Seric extern int Fcnt; 492150Seric 502150Seric /* 512150Seric Set flags for 'fatal' to issue message, call clean-up 522150Seric routine, and terminate processing. 532150Seric */ 542150Seric Fflags = FTLMSG | FTLCLN | FTLEXIT; 552150Seric 562150Seric for(i=1; i<argc; i++) 572150Seric if(argv[i][0] == '-' && (c = argv[i][1])) { 582150Seric p = &argv[i][2]; 592150Seric switch (c) { 602150Seric 612150Seric case 'r': 622150Seric if (!(*p)) 632150Seric fatal("r has no sid (rc11)"); 642150Seric chksid(sid_ab(p,&sid),&sid); 652150Seric break; 662150Seric default: 672150Seric fatal("unknown key letter (cm1)"); 682150Seric } 692150Seric 702150Seric if (had[c - 'a']++) 712150Seric fatal("key letter twice (cm2)"); 722150Seric argv[i] = 0; 732150Seric } 742150Seric else 752150Seric num_files++; 762150Seric 772150Seric if(num_files == 0) 782150Seric fatal("missing file arg (cm3)"); 792150Seric 802150Seric if (*(p = sname(argv[0])) == 'n') 812150Seric p++; 822150Seric if (equal(p,"rmdel")) 832150Seric D_type = 'R'; /* invoked as 'rmdel' */ 842150Seric else if (equal(p,"chghist")) 852150Seric D_type = 'D'; /* invoked as 'chghist' */ 862150Seric else 872150Seric fatal("bad invocation (rc10)"); 882150Seric 892150Seric setsig(); 902150Seric 912150Seric /* 922150Seric Change flags for 'fatal' so that it will return to this 932150Seric routine (main) instead of terminating processing. 942150Seric */ 952150Seric Fflags =& ~FTLEXIT; 962150Seric Fflags =| FTLJMP; 972150Seric 982150Seric /* 992150Seric Call 'rmchg' routine for each file argument. 1002150Seric */ 1012150Seric for (i=1; i<argc; i++) 1022150Seric if (p = argv[i]) 1032150Seric do_file(p,rmchg); 1042150Seric 1052150Seric exit(Fcnt ? 1 : 0); 1062150Seric } 1072150Seric 1082150Seric 1092150Seric /* 1102150Seric Routine that actually causes processing of the delta. 1112150Seric Processing on the file takes place on a 1122150Seric temporary copy of the SCCS file (the x-file). 1132150Seric The name of the x-file is the same as that of the 1142150Seric s-file (SCCS file) with the 's.' replaced by 'x.'. 1152150Seric At end of processing, the s-file is removed 1162150Seric and the x-file is renamed with the name of the old s-file. 1172150Seric 1182150Seric This routine makes use of the z-file to lock out simultaneous 1192150Seric updates to the SCCS file by more than one user. 1202150Seric */ 1212150Seric 1222150Seric struct packet gpkt; /* see file s.h */ 1232150Seric char line[BUFSIZ]; 1242150Seric char *Mrs; 1252150Seric char *Comments; 1262150Seric int Domrs; 1272150Seric 1282150Seric USXALLOC(); /* defines alloc() and free() */ 1292150Seric 1302150Seric rmchg(file) 1312150Seric char *file; 1322150Seric { 1332150Seric static int first_time 1; 1342150Seric struct deltab dt; /* see file s.defines.h */ 1352150Seric struct stats stats; /* see file s.defines.h */ 1362150Seric extern char *Sflags[]; 1372150Seric int n; 1382150Seric char *p, *cp; 1392150Seric int keep; 1402150Seric extern char Pgmr[8]; 1412150Seric int fowner, downer, user; 1422150Seric 1432150Seric if (setjmp(Fjmp)) /* set up to return here from 'fatal' */ 1442150Seric return; /* and return to caller of rmchg */ 1452150Seric 1462150Seric if (!HADR) 1472150Seric fatal("missing r (rc1)"); 1482150Seric 1492150Seric if (D_type == 'D' && first_time) { 1502150Seric first_time = 0; 1512150Seric dohist(file); 1522150Seric } 1532150Seric 1542150Seric if (!exists(file)) 1552150Seric fatal(sprintf(Error,"file %s does not exist (rc2)",file)); 1562150Seric 1572150Seric /* 1582150Seric Lock out any other user who may be trying to process 1592150Seric the same file. 1602150Seric */ 1612150Seric if (lockit(auxf(file,'z'),2,getpid())) 1622150Seric fatal("cannot create lock file (cm4)"); 1632150Seric 1642150Seric sinit(&gpkt,file,1); /* initialize packet and open s-file */ 1652150Seric 1662150Seric /* 1672150Seric Flag for 'putline' routine to tell it to open x-file 1682150Seric and allow writing on it. 1692150Seric */ 1702150Seric gpkt.p_upd = 1; 1712150Seric 1722150Seric /* 1732150Seric Save requested SID for later checking of 1742150Seric permissions (by 'permiss'). 1752150Seric */ 176*19943Ssam bcopy(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid)); 1772150Seric 1782150Seric /* 1792150Seric Now read-in delta table. The 'dodelt' routine 1802150Seric will read the table and change the delta entry of the 1812150Seric requested SID to be of type 'R' if this is 1822150Seric being executed as 'rmdel'; otherwise, for 'chghist', only 1832150Seric the MR and comments sections will be changed 1842150Seric (by 'escdodelt', called by 'dodelt'). 1852150Seric */ 1862150Seric if (dodelt(&gpkt,&stats,&sid,D_type) == 0) 1872150Seric fmterr(&gpkt); 1882150Seric 1892150Seric /* 1902150Seric Get serial number of requested SID from 1912150Seric delta table just processed. 1922150Seric */ 1932150Seric D_serial = sidtoser(&gpkt.p_reqsid,&gpkt); 1942150Seric 1952150Seric /* 1962150Seric If SID has not been zeroed (by 'dodelt'), 1972150Seric SID was not found in file. 1982150Seric */ 1992150Seric if (sid.s_rel != 0) 2002150Seric fatal("nonexistent sid (rc3)"); 2012150Seric /* 2022150Seric Replace 'sid' with original 'sid' 2032150Seric requested. 2042150Seric */ 205*19943Ssam bcopy(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid)); 2062150Seric 2072150Seric /* 2082150Seric Now check permissions. 2092150Seric */ 2102150Seric finduser(&gpkt); 2112150Seric doflags(&gpkt); 2122150Seric permiss(&gpkt); 2132150Seric 2142150Seric /* 2152150Seric Check that user is either owner of file or 2162150Seric directory, or is one who made the delta. 2172150Seric */ 2182150Seric fstat(fileno(gpkt.p_iop),&Statbuf); 2192150Seric fowner = Statbuf.st_uid & 0377; 2202150Seric copy(gpkt.p_file,line); /* temporary for dname() */ 2212150Seric if (stat(dname(line),&Statbuf)) 2222150Seric downer = -1; 2232150Seric else 2242150Seric downer = Statbuf.st_uid & 0377; 2252150Seric user = getuid() & 0377; 2262150Seric if (user != fowner || user != downer) 2272150Seric if (!equal(Pgmr,logname())) 2282150Seric fatal(sprintf(Error, 2292150Seric "you are neither owner nor '%s' (rc4)",Pgmr)); 2302150Seric 2312150Seric /* 2322150Seric For 'rmdel', check that delta being removed is a 2332150Seric 'leaf' delta, and if ok, 2342150Seric process the body. 2352150Seric */ 2362150Seric if (D_type == 'R') { 2372150Seric for (n = maxser(&gpkt); n > D_serial; n--) { 2382150Seric p = &gpkt.p_idel[n]; 2392150Seric if (p->i_pred == D_serial) 2402150Seric fatal("not a 'leaf' delta (rc5)"); 2412150Seric } 2422150Seric 2432150Seric /* 2442150Seric For 'rmdel' check that the sid requested is 2452150Seric not contained in p-file, should a p-file 2462150Seric exist. 2472150Seric */ 2482150Seric 2492150Seric if (exists(auxf(gpkt.p_file,'p'))) 2502150Seric rdpfile(&gpkt,&sid); 2512150Seric 2522150Seric flushto(&gpkt,EUSERTXT,COPY); 2532150Seric 2542150Seric keep = YES; 2552150Seric gpkt.p_chkeof = 1; /* set EOF is ok */ 2562150Seric while ((p = getline(&gpkt)) != NULL) { 2572150Seric if (*p++ == CTLCHAR) { 2582150Seric cp = p++; 2592150Seric NONBLANK(p); 2602150Seric /* 2612150Seric Convert serial number to binary. 2622150Seric */ 2632150Seric if (*(p = satoi(p,&n)) != '\n') 2642150Seric fmterr(&gpkt); 2652150Seric if (n == D_serial) { 2662150Seric gpkt.p_wrttn = 1; 2672150Seric if (*cp == INS) 2682150Seric keep = NO; 2692150Seric else 2702150Seric keep = YES; 2712150Seric } 2722150Seric } 2732150Seric else 2742150Seric if (keep == NO) 2752150Seric gpkt.p_wrttn = 1; 2762150Seric } 2772150Seric } 2782150Seric else { 2792150Seric /* 2802150Seric This is for invocation as 'chghist'. 2812150Seric Check MRs. 2822150Seric */ 2832150Seric if (Mrs) { 2842150Seric if (!(p = Sflags[VALFLAG - 'a'])) 2852150Seric fatal("MRs not allowed (rc6)"); 2862150Seric if (*p && valmrs(&gpkt,p)) 2872150Seric fatal("inavlid MRs (rc7)"); 2882150Seric } 2892150Seric else 2902150Seric if (Sflags[VALFLAG - 'a']) 2912150Seric fatal("MRs required (rc8)"); 2922150Seric 2932150Seric /* 2942150Seric Indicate that EOF at this point is ok, and 2952150Seric flush rest of s-file to x-file. 2962150Seric */ 2972150Seric gpkt.p_chkeof = 1; 2982150Seric while (getline(&gpkt)) 2992150Seric ; 3002150Seric } 3012150Seric 3022150Seric flushline(&gpkt,0); 3032150Seric 3042150Seric /* 3052150Seric Delete old s-file, change x-file name to s-file. 3062150Seric */ 3072150Seric rename(auxf(&gpkt,'x'),&gpkt); 3082150Seric 3092150Seric clean_up(); 3102150Seric } 3112150Seric 3122150Seric 3132150Seric escdodelt(pkt) 3142150Seric struct packet *pkt; 3152150Seric { 3162150Seric extern int First_esc; 3172150Seric char *p; 3182150Seric extern long Timenow; 3192150Seric 3202150Seric if (D_type == 'D' && First_esc) { /* chghist, first time */ 3212150Seric First_esc = 0; 3222150Seric if (Mrs) 3232150Seric putmrs(pkt); 3242150Seric 3252150Seric putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS)); 3262150Seric putline(pkt,Comments); 3272150Seric putline(pkt,"\n"); 3282150Seric putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS)); 3292150Seric putline(pkt,"*** CHANGED *** "); 3302150Seric date_ba(&Timenow,line); /* get date and time */ 3312150Seric putline(pkt,line); 3322150Seric putline(pkt,sprintf(line," %s\n",logname())); 3332150Seric } 3342150Seric 3352150Seric if (pkt->p_line[1] == MRNUM) { 3362150Seric p = &pkt->p_line; 3372150Seric while (*p) 3382150Seric p++; 3392150Seric if (*(p - 2) == DELIVER) 3402150Seric fatal("delta specified has delivered MR (rc9)"); 3412150Seric 3422150Seric if (D_type == 'D') /* turn MRs into comments */ 3432150Seric pkt->p_line[1] = COMMENTS; 3442150Seric } 3452150Seric } 3462150Seric 3472150Seric 3482150Seric putmrs(pkt) 3492150Seric struct packet *pkt; 3502150Seric { 3512150Seric register char **argv; 3522150Seric char str[64]; 3532150Seric extern char *Varg[]; 3542150Seric 3552150Seric for (argv = &Varg[VSTART]; *argv; argv++) 3562150Seric putline(pkt,sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv)); 3572150Seric } 3582150Seric 3592150Seric 3602150Seric clean_up() 3612150Seric { 3622150Seric xrm(&gpkt); 3632150Seric if (gpkt.p_file[0]) 3642150Seric unlockit(auxf(gpkt.p_file,'z'),getpid()); 3652150Seric if (exists(auxf(gpkt.p_file,'x'))) 3662150Seric xunlink(auxf(gpkt.p_file,'x')); 3672150Seric xfreeall(); 3682150Seric } 3692150Seric 3702150Seric 3712150Seric rdpfile(pkt,sp) 3722150Seric register struct packet *pkt; 3732150Seric struct sid *sp; 3742150Seric { 3752150Seric struct pfile pf; 3762150Seric char line[BUFSIZ]; 3772150Seric FILE *in; 3782150Seric 3792150Seric in = xfopen(auxf(pkt->p_file,'p'),0); 3802150Seric while (fgets(line,sizeof(line),in) != NULL) { 3812150Seric pf_ab(line,&pf,1); 3822150Seric if (sp->s_rel == pf.pf_gsid.s_rel && 3832150Seric sp->s_lev == pf.pf_gsid.s_lev && 3842150Seric sp->s_br == pf.pf_gsid.s_br && 3852150Seric sp->s_seq == pf.pf_gsid.s_seq) { 3862150Seric fclose(in); 3872150Seric fatal("being edited -- sid is in p-file (rc12)"); 3882150Seric } 3892150Seric } 3902150Seric fclose(in); 3912150Seric return; 3922150Seric } 393