1 # include "../hdr/defines.h" 2 # include "../hdr/had.h" 3 4 SCCSID(@(#)admin.c 4.1); 5 6 /* 7 Program to create new SCCS files and change parameters 8 of existing ones. Arguments to the program may appear in 9 any order and consist of keyletters, which begin with '-', 10 and named files. Named files which do not exist are created 11 and their parameters are initialized according to the given 12 keyletter arguments, or are given default values if the 13 corresponding keyletters were not supplied. Named files which 14 do exist have those parameters corresponding to given key-letter 15 arguments changed and other parameters are left as is. 16 17 If a directory is given as an argument, each SCCS file within 18 the directory is processed as if it had been specifically named. 19 If a name of '-' is given, the standard input is read for a list 20 of names of SCCS files to be processed. 21 Non-SCCS files are ignored. 22 23 Files created are given mode 444. 24 */ 25 26 # define MINR 1 /* minimum release number */ 27 # define MAXR 9999 /* maximum release number */ 28 # define MAXNAMES 9 29 # define COPY 0 30 # define NOCOPY 1 31 32 char *ifile, *tfile; 33 char *z; /* for validation program name */ 34 char had[26], had_flag[26], rm_flag[26]; 35 char *Comments, *Mrs; 36 char Valpgm[] "/usr/local/val"; 37 int irel, fexists, num_files; 38 int VFLAG 0; 39 int Domrs; 40 char *Sflags[]; 41 char *anames[MAXNAMES], *enames[MAXNAMES]; 42 char *flag_p[26]; 43 int asub, esub; 44 int check_id; 45 int Did_id; 46 47 main(argc,argv) 48 int argc; 49 char *argv[]; 50 { 51 register int j; 52 register char *p; 53 char c, f; 54 int i, testklt; 55 extern admin(); 56 extern int Fcnt; 57 struct sid sid; 58 59 /* 60 Set flags for 'fatal' to issue message, call clean-up 61 routine and terminate processing. 62 */ 63 Fflags = FTLMSG | FTLCLN | FTLEXIT; 64 65 testklt = 1; 66 67 /* 68 The following loop processes keyletters and arguments. 69 Note that these are processed only once for each 70 invocation of 'main'. 71 */ 72 for(j=1; j<argc; j++) 73 if(argv[j][0] == '-' && (c = argv[j][1])) { 74 p = &argv[j][2]; 75 switch (c) { 76 77 case 'i': /* name of file of body */ 78 ifile = p; 79 break; 80 81 case 't': /* name of file of descriptive text */ 82 tfile = p; 83 break; 84 case 'm': /* mr flag */ 85 Mrs = p; 86 break; 87 case 'y': /* comments flag for entry */ 88 Comments = p; 89 break; 90 91 case 'd': /* flags to be deleted */ 92 testklt = 0; 93 if (!(f = *p)) 94 fatal("d has no argument (ad1)"); 95 p = &argv[j][3]; 96 97 switch (f) { 98 99 case IDFLAG: /* see 'f' keyletter */ 100 case BRCHFLAG: /* for meanings of flags */ 101 case VALFLAG: 102 case TYPEFLAG: 103 case MODFLAG: 104 case NULLFLAG: 105 case FLORFLAG: 106 case CEILFLAG: 107 case DEFTFLAG: 108 if (*p) 109 fatal(sprintf(Error, 110 "value after %c flag (ad12)",f)); 111 break; 112 113 default: 114 fatal("unknown flag (ad3)"); 115 } 116 117 if (rm_flag[f - 'a']++) 118 fatal("flag twice (ad4)"); 119 break; 120 121 case 'f': /* flags to be added */ 122 testklt = 0; 123 if (!(f = *p)) 124 fatal("f has no argument (ad5)"); 125 p = &argv[j][3]; 126 127 switch (f) { 128 129 case IDFLAG: /* id-kwd message (err/warn) */ 130 case BRCHFLAG: /* branch */ 131 case NULLFLAG: /* null deltas */ 132 if (*p) 133 fatal(sprintf(Error, 134 "value after %c flag (ad13)",f)); 135 break; 136 137 case VALFLAG: /* mr validation */ 138 VFLAG++; 139 if (*p) 140 z = p; 141 break; 142 143 case FLORFLAG: /* floor */ 144 if ((i = patoi(p)) == -1) 145 fatal("floor not numeric (ad22)"); 146 if ((size(p) > 5) || (i < MINR) || 147 (i > MAXR)) 148 fatal("floor out of range (ad23)"); 149 break; 150 151 case CEILFLAG: /* ceiling */ 152 if ((i = patoi(p)) == -1) 153 fatal("ceiling not numeric (ad24)"); 154 if ((size(p) > 5) || (i < MINR) || 155 (i > MAXR)) 156 fatal("ceiling out of range (ad25)"); 157 break; 158 159 case DEFTFLAG: /* default sid */ 160 if (!(*p)) 161 fatal("no default sid (ad14)"); 162 chksid(sid_ab(p,&sid),&sid); 163 break; 164 165 case TYPEFLAG: /* type */ 166 case MODFLAG: /* module name */ 167 if (!(*p)) 168 fatal(sprintf(Error, 169 "flag %c has no value (ad2)",f)); 170 break; 171 172 default: 173 fatal("unknown flag (ad3)"); 174 } 175 176 if (had_flag[f - 'a']++) 177 fatal("flag twice (ad4)"); 178 flag_p[f - 'a'] = p; 179 break; 180 181 case 'r': /* initial release number supplied */ 182 if ((irel = patoi(p)) == -1) 183 fatal("r arg not numeric (ad6)"); 184 if ((size(p) > 5) || (irel < MINR) || 185 (irel > MAXR)) 186 fatal("r out of range (ad7)"); 187 break; 188 189 case 'n': /* creating new SCCS file */ 190 case 'h': /* only check hash of file */ 191 case 'z': /* zero the input hash */ 192 break; 193 194 case 'a': /* user-name allowed to make deltas */ 195 testklt = 0; 196 if (!(*p)) 197 fatal("bad a argument (ad8)"); 198 if (asub > MAXNAMES) 199 fatal("too many 'a' keyletters (ad9)"); 200 anames[asub++] = p; 201 break; 202 203 case 'e': /* user-name to be removed */ 204 testklt = 0; 205 if (!(*p)) 206 fatal("bad e argument (ad10)"); 207 if (esub > MAXNAMES) 208 fatal("too many 'e' keyletters (ad11)"); 209 enames[esub++] = p; 210 break; 211 212 default: 213 fatal("unknown key letter (cm1)"); 214 } 215 216 if (had[c - 'a']++ && testklt++) 217 fatal("key letter twice (cm2)"); 218 argv[j] = 0; 219 } 220 else 221 num_files++; 222 223 if (num_files == 0) 224 fatal("missing file arg (cm3)"); 225 226 if (HADI && num_files > 1) /* only one file allowed with `i' */ 227 fatal("more than one file (ad15)"); 228 229 setsig(); 230 231 /* 232 Change flags for 'fatal' so that it will return to this 233 routine (main) instead of terminating processing. 234 */ 235 Fflags =& ~FTLEXIT; 236 Fflags =| FTLJMP; 237 238 /* 239 Call 'admin' routine for each file argument. 240 */ 241 for (j=1; j<argc; j++) 242 if (p = argv[j]) 243 do_file(p,admin); 244 245 exit(Fcnt ? 1 : 0); 246 } 247 248 249 /* 250 Routine that actually does admin's work on SCCS files. 251 Existing s-files are copied, with changes being made, to a 252 temporary file (x-file). The name of the x-file is the same as the 253 name of the s-file, with the 's.' replaced by 'x.'. 254 s-files which are to be created are processed in a similar 255 manner, except that a dummy s-file is first created with 256 mode 444. 257 At end of processing, the x-file is renamed with the name of s-file 258 and the old s-file is removed. 259 */ 260 261 struct packet gpkt; /* see file defines.h */ 262 char Zhold[BUFSIZ]; /* temporary z-file name */ 263 264 USXALLOC(); /* defines alloc() and free() */ 265 266 admin(afile) 267 char *afile; 268 { 269 struct deltab dt; /* see file defines.h */ 270 struct stats stats; /* see file defines.h */ 271 FILE *iptr; 272 register int k; 273 register char *cp, *q; 274 char command[80]; 275 char line[512]; 276 int i; /* used in forking procedure */ 277 int status; 278 extern nfiles; 279 extern had_dir; 280 281 if (setjmp(Fjmp)) /* set up to return here from 'fatal' */ 282 return; /* and return to caller of admin */ 283 284 if (HADI && had_dir) /* directory not allowed with `i' keyletter */ 285 fatal("directory named with `i' keyletter (ad26)"); 286 287 fexists = exists(afile); 288 289 if (HADI) 290 HADN = 1; 291 if (HADI || HADN) { 292 if (HADM && !VFLAG) 293 fatal("MRs not allowed (de8)"); 294 295 if (VFLAG && !HADM) 296 fatal("MRs required (de10)"); 297 298 } 299 300 if (!HADI && HADR) 301 fatal("r only allowed with i (ad16)"); 302 303 if (HADN && HADT && !(*tfile)) 304 fatal("t has no argument (ad17)"); 305 306 if (HADN && HADD) 307 fatal("d not allowed with n (ad18)"); 308 309 if (HADN && fexists) 310 fatal(sprintf(Error,"file %s exists (ad19)",afile)); 311 312 if (!HADN && !fexists) 313 fatal(sprintf(Error,"file %s does not exist (ad20)",afile)); 314 /* 315 Check for '-h' flag. If set, create child process and 316 invoke 'get' to examine format of SCCS file. 317 */ 318 319 if (HADH) { 320 /* 321 fork here so 'admin' can execute 'val' to 322 check for a corrupted file. 323 */ 324 if ((i = fork()) < 0) 325 fatal("cannot fork, try again"); 326 if (i == 0) { /* child */ 327 /* 328 perform 'val' with appropriate keyletters 329 */ 330 execl("/bin/sh","/bin/sh","-c", 331 sprintf(command, 332 "/usr/local/val -s %s", 333 afile),0); 334 fatal(sprintf(Error,"cannot execute '%s'",Valpgm)); 335 } 336 else { 337 wait(&status); /* wait on status from 'execl' */ 338 if (status) 339 fatal("corrupted file (co6)"); 340 return; /* return to caller of 'admin' */ 341 } 342 } 343 344 /* 345 Lock out any other user who may be trying to process 346 the same file. 347 */ 348 if (!HADH && lockit(copy(auxf(afile,'z'),Zhold),2,getpid())) 349 fatal("cannot create lock file (cm4)"); 350 351 if (fexists) 352 sinit(&gpkt,afile,1); /* init pkt & open s-file */ 353 else { 354 xfcreat(afile,0444); /* create dummy s-file */ 355 sinit(&gpkt,afile,0); /* and init pkt */ 356 } 357 358 if (!HADH) 359 /* 360 set the flag for 'putline' routine to open 361 the 'x-file' and allow writing on it. 362 */ 363 gpkt.p_upd = 1; 364 365 if (HADZ) { 366 gpkt.do_chksum = 0; /* ignore checksum processing */ 367 gpkt.p_ihash = 0; 368 } 369 370 /* 371 Get statistics of latest delta in old file. 372 */ 373 if (!HADN) { 374 stats_ab(&gpkt,&stats); 375 gpkt.p_wrttn++; 376 newstats(&gpkt,line,"0"); 377 } 378 379 if (HADN) { /* N E W F I L E */ 380 381 /* 382 Beginning of SCCS file. 383 */ 384 putline(&gpkt,sprintf(line,"%c%c%s\n",CTLCHAR,HEAD,"00000")); 385 386 /* 387 Statistics. 388 */ 389 newstats(&gpkt,line,"0"); 390 391 dt.d_type = 'D'; /* type of delta */ 392 393 /* 394 Set initial release, level, branch and 395 sequence values. 396 */ 397 if (HADR) 398 dt.d_sid.s_rel = irel; 399 else 400 dt.d_sid.s_rel = 1; 401 dt.d_sid.s_lev = 1; 402 dt.d_sid.s_br = dt.d_sid.s_seq = 0; 403 404 time(&dt.d_datetime); /* get time and date */ 405 406 copy(logname(),dt.d_pgmr); /* get user's name */ 407 408 dt.d_serial = 1; 409 dt.d_pred = 0; 410 411 del_ba(&dt,line); /* form and write */ 412 putline(&gpkt,line); /* delta-table entry */ 413 414 /* 415 If -m flag, enter MR numbers 416 */ 417 418 if (Mrs) { 419 mrfixup(); 420 if (z && valmrs(&gpkt,z)) 421 fatal("invalid MRs (de9)"); 422 putmrs(&gpkt); 423 } 424 425 /* 426 Enter comment line for `chghist' 427 */ 428 429 if (HADY) { 430 putline(&gpkt,sprintf(line,"%c%c ",CTLCHAR,COMMENTS)); 431 putline(&gpkt,Comments); 432 putline(&gpkt,"\n"); 433 } 434 else { 435 /* 436 insert date/time and pgmr into comment line 437 */ 438 cmt_ba(&dt,line); 439 putline(&gpkt,line); 440 } 441 /* 442 End of delta-table. 443 */ 444 putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,EDELTAB)); 445 446 /* 447 Beginning of user-name section. 448 */ 449 putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,BUSERNAM)); 450 } 451 else 452 /* 453 For old file, copy to x-file until user-name section 454 is found. 455 */ 456 flushto(&gpkt,BUSERNAM,COPY); 457 458 /* 459 Write user-names to be added to list of those 460 allowed to make deltas. 461 */ 462 if (HADA) 463 for (k = 0; k < asub; k++) 464 putline(&gpkt,sprintf(line,"%s\n",anames[k])); 465 466 /* 467 Do not copy those user-names which are to be erased. 468 */ 469 if (HADE && !HADN) 470 while ((cp = getline(&gpkt)) && 471 !(*cp++ == CTLCHAR && *cp == EUSERNAM)) { 472 for (k = 0; k < esub; k++) { 473 cp = &gpkt.p_line; 474 while (*cp) /* find and */ 475 cp++; /* zero newline */ 476 *--cp = '\0'; /* character */ 477 478 if (equal(enames[k],&gpkt.p_line)) { 479 /* 480 Tell getline not to output 481 previously read line. 482 */ 483 gpkt.p_wrttn = 1; 484 break; 485 } 486 else 487 *cp = '\n'; /* restore newline */ 488 } 489 } 490 491 if (HADN) { /* N E W F I L E */ 492 493 /* 494 End of user-name section. 495 */ 496 putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,EUSERNAM)); 497 } 498 else 499 /* 500 For old file, copy to x-file until end of 501 user-names section is found. 502 */ 503 if (!HADE) 504 flushto(&gpkt,EUSERNAM,COPY); 505 506 /* 507 For old file, read flags and their values (if any), and 508 store them. Check to see if the flag read is one that 509 should be deleted. 510 */ 511 if (!HADN) 512 while ((cp = getline(&gpkt)) && 513 (*cp++ == CTLCHAR && *cp == FLAG)) { 514 515 gpkt.p_wrttn = 1; /* don't write previous line */ 516 517 cp =+ 2; /* point to flag character */ 518 k = *cp - 'a'; 519 520 if (!had_flag[k] && !rm_flag[k]) { 521 had_flag[k] = 2; /* indicate flag is */ 522 /* from file, not */ 523 /* from arg list */ 524 525 if (*++cp != '\n') { /* get flag value */ 526 q = alloc(size(gpkt.p_line)-5); 527 copy(++cp,q); 528 flag_p[k] = q; 529 while (*q) /* find and */ 530 q++; /* zero newline */ 531 *--q = '\0'; /* character */ 532 } 533 } 534 else 535 if (rm_flag[k]) 536 had_flag[k] = 0; 537 } 538 539 540 /* 541 Write out flags. 542 */ 543 for (k = 0; k < 26; k++) 544 if (had_flag[k]) { 545 if (flag_p[k]) 546 sprintf(line,"%c%c %c %s\n", 547 CTLCHAR,FLAG,'a'+k,flag_p[k]); 548 else 549 sprintf(line,"%c%c %c\n", 550 CTLCHAR,FLAG,'a'+k); 551 552 putline(&gpkt,line); 553 554 if (had_flag[k] == 2) { /* flag was taken from file */ 555 had_flag[k] = 0; 556 if (flag_p[k]) { 557 free(flag_p[k]); 558 flag_p[k] = 0; 559 } 560 } 561 } 562 563 if (HADN) 564 /* 565 Beginning of descriptive (user) text. 566 */ 567 putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,BUSERTXT)); 568 else 569 /* 570 Write out BUSERTXT record which was read in 571 above loop that processes flags. 572 */ 573 gpkt.p_wrttn = 0; 574 putline(&gpkt,0); 575 576 /* 577 Get user description, copy to x-file. 578 */ 579 if (HADT) { 580 if (*tfile) { 581 iptr = xfopen(tfile,0); 582 fgetchk(line,512,iptr,tfile,&gpkt); 583 fclose(iptr); 584 } 585 586 /* 587 If old file, ignore any previously supplied 588 commentary. (i.e., don't copy it to x-file.) 589 */ 590 if (!HADN) 591 flushto(&gpkt,EUSERTXT,NOCOPY); 592 } 593 594 if (HADN) { /* N E W F I L E */ 595 596 /* 597 End of user description. 598 */ 599 putline(&gpkt,sprintf(line,CTLSTR,CTLCHAR,EUSERTXT)); 600 601 /* 602 Beginning of body (text) of first delta. 603 */ 604 putline(&gpkt,sprintf(line,"%c%c %u\n",CTLCHAR,INS,1)); 605 606 if (HADI) { /* get body */ 607 608 /* 609 Set indicator to check lines of body of file for 610 keyword definitions. 611 If no keywords are found, a warning 612 will be produced. 613 */ 614 check_id = 1; 615 /* 616 Set indicator that tells whether there 617 were any keywords to 'no'. 618 */ 619 Did_id = 0; 620 if (*ifile) 621 iptr = xfopen(ifile,0); /* from a file */ 622 else 623 iptr = stdin; /* from standard input */ 624 625 /* 626 Read and copy to x-file, while checking 627 first character of each line to see that it 628 is not the control character (octal 1). 629 Also, count lines read, and set statistics' 630 structure appropriately. 631 The 'fgetchk' routine will check for keywords. 632 */ 633 stats.s_ins = fgetchk(line,512,iptr,ifile,&gpkt); 634 stats.s_del = stats.s_unc = 0; 635 636 /* 637 If no keywords were found, issue warning. 638 */ 639 if (!Did_id) { 640 if (had_flag[IDFLAG - 'a']) 641 fatal("no id keywords (cm6)"); 642 else 643 fprintf(stderr,"%s\n","No id keywords (cm7)"); 644 } 645 646 check_id = 0; 647 Did_id = 0; 648 } 649 650 /* 651 End of body of first delta. 652 */ 653 putline(&gpkt,sprintf(line,"%c%c %u\n",CTLCHAR,END,1)); 654 } 655 else { 656 /* 657 Indicate that EOF at this point is ok, and 658 flush rest of (old) s-file to x-file. 659 */ 660 gpkt.p_chkeof = 1; 661 while (getline(&gpkt)) ; 662 } 663 664 /* 665 Flush the buffer, take care of rewinding to insert 666 checksum and statistics in file, and close. 667 */ 668 flushline(&gpkt,&stats); 669 670 /* 671 Change x-file name to s-file, and delete old file. 672 Unlock file before returning. 673 */ 674 if (!HADH) { 675 rename(auxf(&gpkt,'x'),&gpkt); 676 xrm(&gpkt); 677 unlockit(auxf(afile,'z'),getpid()); 678 } 679 } 680 681 682 fgetchk(strp,len,inptr,file,pkt) 683 register char *strp; 684 register int len; 685 FILE *inptr; 686 register char *file; 687 register struct packet *pkt; 688 { 689 register int k; 690 691 for (k = 1; fgets(strp,len,inptr); k++) { 692 if (*strp == CTLCHAR) fatal( 693 sprintf(Error,"%s illegal data on line %d (ad21)", 694 file,k)); 695 696 if (check_id) 697 chkid(strp); 698 699 putline(pkt,strp); 700 } 701 return(k - 1); 702 } 703 704 705 clean_up() 706 { 707 xrm(&gpkt); 708 if (!HADH) 709 unlockit(Zhold,getpid()); 710 if (HADN) 711 unlink(&gpkt); 712 } 713 714 715 cmt_ba(dt,str) 716 register struct deltab *dt; 717 char *str; 718 { 719 register char *p; 720 721 p = str; 722 *p++ = CTLCHAR; 723 *p++ = COMMENTS; 724 *p++ = ' '; 725 copy("date and time created",p); 726 while (*p++) 727 ; 728 --p; 729 *p++ = ' '; 730 date_ba(&dt->d_datetime,p); 731 while (*p++) 732 ; 733 --p; 734 *p++ = ' '; 735 copy("by",p); 736 while (*p++) 737 ; 738 --p; 739 *p++ = ' '; 740 copy(dt->d_pgmr,p); 741 while (*p++) 742 ; 743 --p; 744 *p++ = '\n'; 745 *p = 0; 746 return(str); 747 } 748 749 750 putmrs(pkt) 751 struct packet *pkt; 752 { 753 register char **argv; 754 char str[64]; 755 extern char *Varg[]; 756 757 for (argv = &Varg[VSTART]; *argv; argv++) 758 putline(pkt,sprintf(str,"%c%c %s\n",CTLCHAR,MRNUM,*argv)); 759 } 760