1 # include "../hdr/defines.h" 2 # include "../hdr/had.h" 3 4 static char Sccsid[] = "@(#)rmchg.c 4.4 02/02/88"; 5 6 /* 7 Program to remove a specified delta from an SCCS file, 8 when invoked as 'rmdel', 9 or to change the MRs and/or comments of a specified delta, 10 when invoked as 'chghist'. 11 (The program has two links to it, one called 'rmdel', the 12 other 'chghist'.) 13 14 The delta to be removed (or whose MRs and/or comments 15 are to be changed) is specified via the 16 r argument, in the form of an SID. 17 18 If the delta is to be removed, it must be the most recent one 19 in its branch in the delta tree (a so-called 'leaf' delta). 20 For either function, the delta being processed must not 21 have any 'delivered' MRs, and the user must have basically 22 the same permissions as are required to make deltas. 23 24 If a directory is given as an argument, each SCCS file 25 within the directory will be processed as if it had been 26 specifically named. If a name of '-' is given, the standard 27 input will be read for a list of names of SCCS files to be 28 processed. Non SCCS files are ignored. 29 */ 30 31 # define COPY 0 32 # define NOCOPY 1 33 34 struct sid sid; 35 int num_files; 36 char had[26]; 37 char D_type; 38 int D_serial; 39 40 main(argc,argv) 41 int argc; 42 char *argv[]; 43 { 44 register int i; 45 register char *p; 46 char c; 47 extern rmchg(); 48 extern int Fcnt; 49 50 /* 51 Set flags for 'fatal' to issue message, call clean-up 52 routine, and terminate processing. 53 */ 54 Fflags = FTLMSG | FTLCLN | FTLEXIT; 55 56 for(i=1; i<argc; i++) 57 if(argv[i][0] == '-' && (c = argv[i][1])) { 58 p = &argv[i][2]; 59 switch (c) { 60 61 case 'r': 62 if (!(*p)) 63 fatal("r has no sid (rc11)"); 64 chksid(sid_ab(p,&sid),&sid); 65 break; 66 default: 67 fatal("unknown key letter (cm1)"); 68 } 69 70 if (had[c - 'a']++) 71 fatal("key letter twice (cm2)"); 72 argv[i] = 0; 73 } 74 else 75 num_files++; 76 77 if(num_files == 0) 78 fatal("missing file arg (cm3)"); 79 80 if (*(p = sname(argv[0])) == 'n') 81 p++; 82 if (equal(p,"rmdel")) 83 D_type = 'R'; /* invoked as 'rmdel' */ 84 else if (equal(p,"chghist")) 85 D_type = 'D'; /* invoked as 'chghist' */ 86 else 87 fatal("bad invocation (rc10)"); 88 89 setsig(); 90 91 /* 92 Change flags for 'fatal' so that it will return to this 93 routine (main) instead of terminating processing. 94 */ 95 Fflags &= ~FTLEXIT; 96 Fflags |= FTLJMP; 97 98 /* 99 Call 'rmchg' routine for each file argument. 100 */ 101 for (i=1; i<argc; i++) 102 if (p = argv[i]) 103 do_file(p,rmchg); 104 105 exit(Fcnt ? 1 : 0); 106 } 107 108 109 /* 110 Routine that actually causes processing of the delta. 111 Processing on the file takes place on a 112 temporary copy of the SCCS file (the x-file). 113 The name of the x-file is the same as that of the 114 s-file (SCCS file) with the 's.' replaced by 'x.'. 115 At end of processing, the s-file is removed 116 and the x-file is renamed with the name of the old s-file. 117 118 This routine makes use of the z-file to lock out simultaneous 119 updates to the SCCS file by more than one user. 120 */ 121 122 struct packet gpkt; /* see file s.h */ 123 char line[BUFSIZ]; 124 char *Mrs; 125 char *Comments; 126 int Domrs; 127 128 USXALLOC(); /* defines alloc() and free() */ 129 130 rmchg(file) 131 char *file; 132 { 133 static int first_time = 1; 134 struct deltab dt; /* see file s.defines.h */ 135 struct stats stats; /* see file s.defines.h */ 136 extern char *Sflags[]; 137 int n; 138 char *p, *cp; 139 int keep; 140 extern char Pgmr[8]; 141 int fowner, downer, user; 142 143 if (setjmp(Fjmp)) /* set up to return here from 'fatal' */ 144 return; /* and return to caller of rmchg */ 145 146 if (!HADR) 147 fatal("missing r (rc1)"); 148 149 if (D_type == 'D' && first_time) { 150 first_time = 0; 151 dohist(file); 152 } 153 154 if (!exists(file)) { 155 sprintf(Error,"file %s does not exist (rc2)",file); 156 fatal(Error); 157 } 158 159 /* 160 Lock out any other user who may be trying to process 161 the same file. 162 */ 163 if (lockit(auxf(file,'z'),2,getpid())) 164 fatal("cannot create lock file (cm4)"); 165 166 sinit(&gpkt,file,1); /* initialize packet and open s-file */ 167 168 /* 169 Flag for 'putline' routine to tell it to open x-file 170 and allow writing on it. 171 */ 172 gpkt.p_upd = 1; 173 174 /* 175 Save requested SID for later checking of 176 permissions (by 'permiss'). 177 */ 178 bcopy(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid)); 179 180 /* 181 Now read-in delta table. The 'dodelt' routine 182 will read the table and change the delta entry of the 183 requested SID to be of type 'R' if this is 184 being executed as 'rmdel'; otherwise, for 'chghist', only 185 the MR and comments sections will be changed 186 (by 'escdodelt', called by 'dodelt'). 187 */ 188 if (dodelt(&gpkt,&stats,&sid,D_type) == 0) 189 fmterr(&gpkt); 190 191 /* 192 Get serial number of requested SID from 193 delta table just processed. 194 */ 195 D_serial = sidtoser(&gpkt.p_reqsid,&gpkt); 196 197 /* 198 If SID has not been zeroed (by 'dodelt'), 199 SID was not found in file. 200 */ 201 if (sid.s_rel != 0) 202 fatal("nonexistent sid (rc3)"); 203 /* 204 Replace 'sid' with original 'sid' 205 requested. 206 */ 207 bcopy(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid)); 208 209 /* 210 Now check permissions. 211 */ 212 finduser(&gpkt); 213 doflags(&gpkt); 214 permiss(&gpkt); 215 216 /* 217 Check that user is either owner of file or 218 directory, or is one who made the delta. 219 */ 220 fstat(fileno(gpkt.p_iop),&Statbuf); 221 fowner = Statbuf.st_uid & 0377; 222 copy(gpkt.p_file,line); /* temporary for dname() */ 223 if (stat(dname(line),&Statbuf)) 224 downer = -1; 225 else 226 downer = Statbuf.st_uid & 0377; 227 user = getuid() & 0377; 228 if (user != fowner || user != downer) 229 if (!equal(Pgmr,logname())) { 230 sprintf(Error, "you are neither owner nor '%s' (rc4)",Pgmr); 231 fatal(Error); 232 } 233 234 /* 235 For 'rmdel', check that delta being removed is a 236 'leaf' delta, and if ok, 237 process the body. 238 */ 239 if (D_type == 'R') { 240 for (n = maxser(&gpkt); n > D_serial; n--) { 241 p = &gpkt.p_idel[n]; 242 if (p->i_pred == D_serial) 243 fatal("not a 'leaf' delta (rc5)"); 244 } 245 246 /* 247 For 'rmdel' check that the sid requested is 248 not contained in p-file, should a p-file 249 exist. 250 */ 251 252 if (exists(auxf(gpkt.p_file,'p'))) 253 rdpfile(&gpkt,&sid); 254 255 flushto(&gpkt,EUSERTXT,COPY); 256 257 keep = YES; 258 gpkt.p_chkeof = 1; /* set EOF is ok */ 259 while ((p = getline(&gpkt)) != NULL) { 260 if (*p++ == CTLCHAR) { 261 cp = p++; 262 NONBLANK(p); 263 /* 264 Convert serial number to binary. 265 */ 266 if (*(p = satoi(p,&n)) != '\n') 267 fmterr(&gpkt); 268 if (n == D_serial) { 269 gpkt.p_wrttn = 1; 270 if (*cp == INS) 271 keep = NO; 272 else 273 keep = YES; 274 } 275 } 276 else 277 if (keep == NO) 278 gpkt.p_wrttn = 1; 279 } 280 } 281 else { 282 /* 283 This is for invocation as 'chghist'. 284 Check MRs. 285 */ 286 if (Mrs) { 287 if (!(p = Sflags[VALFLAG - 'a'])) 288 fatal("MRs not allowed (rc6)"); 289 if (*p && valmrs(&gpkt,p)) 290 fatal("inavlid MRs (rc7)"); 291 } 292 else 293 if (Sflags[VALFLAG - 'a']) 294 fatal("MRs required (rc8)"); 295 296 /* 297 Indicate that EOF at this point is ok, and 298 flush rest of s-file to x-file. 299 */ 300 gpkt.p_chkeof = 1; 301 while (getline(&gpkt)) 302 ; 303 } 304 305 flushline(&gpkt,0); 306 307 /* 308 Delete old s-file, change x-file name to s-file. 309 */ 310 rename(auxf(&gpkt,'x'),&gpkt); 311 312 clean_up(); 313 } 314 315 316 escdodelt(pkt) 317 struct packet *pkt; 318 { 319 extern int First_esc; 320 char *p; 321 extern long Timenow; 322 323 if (D_type == 'D' && First_esc) { /* chghist, first time */ 324 First_esc = 0; 325 if (Mrs) 326 putmrs(pkt); 327 328 sprintf(line,"%c%c ",CTLCHAR,COMMENTS); 329 putline(pkt,line); 330 putline(pkt,Comments); 331 putline(pkt,"\n"); 332 sprintf(line,"%c%c ",CTLCHAR,COMMENTS); 333 putline(pkt,line); 334 putline(pkt,"*** CHANGED *** "); 335 date_ba(&Timenow,line); /* get date and time */ 336 putline(pkt,line); 337 sprintf(line," %s\n",logname()); 338 putline(pkt,line); 339 } 340 341 if (pkt->p_line[1] == MRNUM) { 342 p = &pkt->p_line; 343 while (*p) 344 p++; 345 if (*(p - 2) == DELIVER) 346 fatal("delta specified has delivered MR (rc9)"); 347 348 if (D_type == 'D') /* turn MRs into comments */ 349 pkt->p_line[1] = COMMENTS; 350 } 351 } 352 353 354 putmrs(pkt) 355 struct packet *pkt; 356 { 357 register char **argv; 358 char str[64]; 359 extern char *Varg[]; 360 361 for (argv = &Varg[VSTART]; *argv; argv++) { 362 sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv); 363 putline(pkt,str); 364 } 365 } 366 367 368 clean_up() 369 { 370 xrm(&gpkt); 371 if (gpkt.p_file[0]) 372 unlockit(auxf(gpkt.p_file,'z'),getpid()); 373 if (exists(auxf(gpkt.p_file,'x'))) 374 xunlink(auxf(gpkt.p_file,'x')); 375 xfreeall(); 376 } 377 378 379 rdpfile(pkt,sp) 380 register struct packet *pkt; 381 struct sid *sp; 382 { 383 struct pfile pf; 384 char line[BUFSIZ]; 385 FILE *in; 386 387 in = xfopen(auxf(pkt->p_file,'p'),0); 388 while (fgets(line,sizeof(line),in) != NULL) { 389 pf_ab(line,&pf,1); 390 if (sp->s_rel == pf.pf_gsid.s_rel && 391 sp->s_lev == pf.pf_gsid.s_lev && 392 sp->s_br == pf.pf_gsid.s_br && 393 sp->s_seq == pf.pf_gsid.s_seq) { 394 fclose(in); 395 fatal("being edited -- sid is in p-file (rc12)"); 396 } 397 } 398 fclose(in); 399 return; 400 } 401