1 # include "../hdr/defines.h"
2 # include "../hdr/had.h"
3
4 static char Sccsid[] = "@(#)admin.c 4.3 02/02/88";
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
main(argc,argv)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
admin(afile)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, "/usr/local/val -s %s", afile);
338 execl("/bin/sh","/bin/sh","-c", command, 0);
339 sprintf(Error,"cannot execute '%s'",Valpgm);
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
fgetchk(strp,len,inptr,file,pkt)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
clean_up()725 clean_up()
726 {
727 xrm(&gpkt);
728 if (!HADH)
729 unlockit(Zhold,getpid());
730 if (HADN)
731 unlink(&gpkt);
732 }
733
734
cmt_ba(dt,str)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