1 # include "../hdr/defines.h" 2 # include "../hdr/had.h" 3 4 SCCSID(@(#)rmchg.c 4.1); 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 fatal(sprintf(Error,"file %s does not exist (rc2)",file)); 156 157 /* 158 Lock out any other user who may be trying to process 159 the same file. 160 */ 161 if (lockit(auxf(file,'z'),2,getpid())) 162 fatal("cannot create lock file (cm4)"); 163 164 sinit(&gpkt,file,1); /* initialize packet and open s-file */ 165 166 /* 167 Flag for 'putline' routine to tell it to open x-file 168 and allow writing on it. 169 */ 170 gpkt.p_upd = 1; 171 172 /* 173 Save requested SID for later checking of 174 permissions (by 'permiss'). 175 */ 176 move(&sid,&gpkt.p_reqsid,sizeof(gpkt.p_reqsid)); 177 178 /* 179 Now read-in delta table. The 'dodelt' routine 180 will read the table and change the delta entry of the 181 requested SID to be of type 'R' if this is 182 being executed as 'rmdel'; otherwise, for 'chghist', only 183 the MR and comments sections will be changed 184 (by 'escdodelt', called by 'dodelt'). 185 */ 186 if (dodelt(&gpkt,&stats,&sid,D_type) == 0) 187 fmterr(&gpkt); 188 189 /* 190 Get serial number of requested SID from 191 delta table just processed. 192 */ 193 D_serial = sidtoser(&gpkt.p_reqsid,&gpkt); 194 195 /* 196 If SID has not been zeroed (by 'dodelt'), 197 SID was not found in file. 198 */ 199 if (sid.s_rel != 0) 200 fatal("nonexistent sid (rc3)"); 201 /* 202 Replace 'sid' with original 'sid' 203 requested. 204 */ 205 move(&gpkt.p_reqsid,&sid,sizeof(gpkt.p_reqsid)); 206 207 /* 208 Now check permissions. 209 */ 210 finduser(&gpkt); 211 doflags(&gpkt); 212 permiss(&gpkt); 213 214 /* 215 Check that user is either owner of file or 216 directory, or is one who made the delta. 217 */ 218 fstat(fileno(gpkt.p_iop),&Statbuf); 219 fowner = Statbuf.st_uid & 0377; 220 copy(gpkt.p_file,line); /* temporary for dname() */ 221 if (stat(dname(line),&Statbuf)) 222 downer = -1; 223 else 224 downer = Statbuf.st_uid & 0377; 225 user = getuid() & 0377; 226 if (user != fowner || user != downer) 227 if (!equal(Pgmr,logname())) 228 fatal(sprintf(Error, 229 "you are neither owner nor '%s' (rc4)",Pgmr)); 230 231 /* 232 For 'rmdel', check that delta being removed is a 233 'leaf' delta, and if ok, 234 process the body. 235 */ 236 if (D_type == 'R') { 237 for (n = maxser(&gpkt); n > D_serial; n--) { 238 p = &gpkt.p_idel[n]; 239 if (p->i_pred == D_serial) 240 fatal("not a 'leaf' delta (rc5)"); 241 } 242 243 /* 244 For 'rmdel' check that the sid requested is 245 not contained in p-file, should a p-file 246 exist. 247 */ 248 249 if (exists(auxf(gpkt.p_file,'p'))) 250 rdpfile(&gpkt,&sid); 251 252 flushto(&gpkt,EUSERTXT,COPY); 253 254 keep = YES; 255 gpkt.p_chkeof = 1; /* set EOF is ok */ 256 while ((p = getline(&gpkt)) != NULL) { 257 if (*p++ == CTLCHAR) { 258 cp = p++; 259 NONBLANK(p); 260 /* 261 Convert serial number to binary. 262 */ 263 if (*(p = satoi(p,&n)) != '\n') 264 fmterr(&gpkt); 265 if (n == D_serial) { 266 gpkt.p_wrttn = 1; 267 if (*cp == INS) 268 keep = NO; 269 else 270 keep = YES; 271 } 272 } 273 else 274 if (keep == NO) 275 gpkt.p_wrttn = 1; 276 } 277 } 278 else { 279 /* 280 This is for invocation as 'chghist'. 281 Check MRs. 282 */ 283 if (Mrs) { 284 if (!(p = Sflags[VALFLAG - 'a'])) 285 fatal("MRs not allowed (rc6)"); 286 if (*p && valmrs(&gpkt,p)) 287 fatal("inavlid MRs (rc7)"); 288 } 289 else 290 if (Sflags[VALFLAG - 'a']) 291 fatal("MRs required (rc8)"); 292 293 /* 294 Indicate that EOF at this point is ok, and 295 flush rest of s-file to x-file. 296 */ 297 gpkt.p_chkeof = 1; 298 while (getline(&gpkt)) 299 ; 300 } 301 302 flushline(&gpkt,0); 303 304 /* 305 Delete old s-file, change x-file name to s-file. 306 */ 307 rename(auxf(&gpkt,'x'),&gpkt); 308 309 clean_up(); 310 } 311 312 313 escdodelt(pkt) 314 struct packet *pkt; 315 { 316 extern int First_esc; 317 char *p; 318 extern long Timenow; 319 320 if (D_type == 'D' && First_esc) { /* chghist, first time */ 321 First_esc = 0; 322 if (Mrs) 323 putmrs(pkt); 324 325 putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS)); 326 putline(pkt,Comments); 327 putline(pkt,"\n"); 328 putline(pkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS)); 329 putline(pkt,"*** CHANGED *** "); 330 date_ba(&Timenow,line); /* get date and time */ 331 putline(pkt,line); 332 putline(pkt,sprintf(line," %s\n",logname())); 333 } 334 335 if (pkt->p_line[1] == MRNUM) { 336 p = &pkt->p_line; 337 while (*p) 338 p++; 339 if (*(p - 2) == DELIVER) 340 fatal("delta specified has delivered MR (rc9)"); 341 342 if (D_type == 'D') /* turn MRs into comments */ 343 pkt->p_line[1] = COMMENTS; 344 } 345 } 346 347 348 putmrs(pkt) 349 struct packet *pkt; 350 { 351 register char **argv; 352 char str[64]; 353 extern char *Varg[]; 354 355 for (argv = &Varg[VSTART]; *argv; argv++) 356 putline(pkt,sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv)); 357 } 358 359 360 clean_up() 361 { 362 xrm(&gpkt); 363 if (gpkt.p_file[0]) 364 unlockit(auxf(gpkt.p_file,'z'),getpid()); 365 if (exists(auxf(gpkt.p_file,'x'))) 366 xunlink(auxf(gpkt.p_file,'x')); 367 xfreeall(); 368 } 369 370 371 rdpfile(pkt,sp) 372 register struct packet *pkt; 373 struct sid *sp; 374 { 375 struct pfile pf; 376 char line[BUFSIZ]; 377 FILE *in; 378 379 in = xfopen(auxf(pkt->p_file,'p'),0); 380 while (fgets(line,sizeof(line),in) != NULL) { 381 pf_ab(line,&pf,1); 382 if (sp->s_rel == pf.pf_gsid.s_rel && 383 sp->s_lev == pf.pf_gsid.s_lev && 384 sp->s_br == pf.pf_gsid.s_br && 385 sp->s_seq == pf.pf_gsid.s_seq) { 386 fclose(in); 387 fatal("being edited -- sid is in p-file (rc12)"); 388 } 389 } 390 fclose(in); 391 return; 392 } 393