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