1 /************************************************************************/ 2 /* */ 3 /* val - */ 4 /* val [-mname] [-rSID] [-s] [-ytype] file ... */ 5 /* */ 6 /************************************************************************/ 7 8 # include "../hdr/defines.h" 9 # include "../hdr/had.h" 10 11 # define FILARG_ERR 0200 /* no file name given */ 12 # define UNKDUP_ERR 0100 /* unknown or duplicate keyletter */ 13 # define CORRUPT_ERR 040 /* corrupt file error code */ 14 # define FILENAM_ERR 020 /* file name error code */ 15 # define INVALSID_ERR 010 /* invalid or ambiguous SID error */ 16 # define NONEXSID_ERR 04 /* non-existent SID error code */ 17 # define TYPE_ERR 02 /* type arg value error code */ 18 # define NAME_ERR 01 /* name arg value error code */ 19 # define TRUE 1 20 # define FALSE 0 21 # define BLANK(l) while (!(*l == ' ' || *l == '\t')) l++; 22 23 int ret_code; /* prime return code from 'main' program */ 24 int inline_err; /* input line error code (from 'process') */ 25 int infile_err; /* file error code (from 'validate') */ 26 int inpstd; /* TRUE = args from standard input */ 27 28 struct packet gpkt; 29 30 char had[26]; /* had flag used in 'process' function */ 31 char path[50]; /* storage for file name value */ 32 char sid[50]; /* storage for sid (-r) value */ 33 char type[50]; /* storage for type (-y) value */ 34 char name[50]; /* storage for name (-m) value */ 35 char line[BUFSIZ]; 36 char *get_line(); /* function returning ptr to line read */ 37 char *getval(); /* function returning adjusted ptr to line */ 38 char *alloc(); /* function returning ptr */ 39 char *fgets(); /* function returning i/o ptr */ 40 41 struct delent { /* structure for delta table entry */ 42 char type; 43 char *osid; 44 char *datetime; 45 char *pgmr; 46 char *serial; 47 char *pred; 48 } del; 49 50 SCCSID(@(#)val.c 4.1); 51 52 /* This is the main program that determines whether the command line 53 * comes from the standard input or read off the original command 54 * line. See VAL(I) for more information. 55 */ 56 main(argc,argv) 57 int argc; 58 char *argv[]; 59 { 60 FILE *iop; 61 register int j; 62 63 ret_code = 0; 64 if (argc == 2 && argv[1][0] == '-' && !(argv[1][1])) { 65 inpstd = TRUE; 66 iop = stdin; /* read from standard input */ 67 while (fgets(line,BUFSIZ,iop) != NULL) { 68 if (line[0] != '\n') { 69 repl (line,'\n','\0'); 70 process(line); 71 ret_code =| inline_err; 72 } 73 } 74 } 75 else { 76 inpstd = FALSE; 77 for (j = 1; j < argc; j++) 78 sprintf(&(line[strlen(line)]),"%s ",argv[j]); 79 j = strlen(line) - 1; 80 line[j > 0 ? j : 0] = NULL; 81 process(line); 82 ret_code = inline_err; 83 } 84 exit(ret_code); 85 } 86 87 88 /* This function processes the line sent by the main routine. It 89 * determines which keyletter values are present on the command 90 * line and assigns the values to the correct storage place. It 91 * then calls validate for each file name on the command line 92 * It will return to main if the input line contains an error, 93 * otherwise it returns any error code found by validate. 94 */ 95 process(p_line) 96 char *p_line; 97 { 98 register int j; 99 register int testklt; 100 register int line_sw; 101 102 int silent; 103 int num_files; 104 105 char filelist[50][50]; 106 char *savelinep; 107 char c; 108 109 silent = FALSE; 110 path[0] = sid[0] = type[0] = name[0] = 0; 111 num_files = inline_err = 0; 112 113 /* 114 make copy of 'line' for use later 115 */ 116 savelinep = p_line; 117 /* 118 clear out had flags for each 'line' processed 119 */ 120 for (j = 0; j < 27; j++) 121 had[j] = 0; 122 /* 123 execute loop until all characters in 'line' are checked. 124 */ 125 while (*p_line) { 126 testklt = 1; 127 NONBLANK(p_line); 128 if (*p_line == '-') { 129 p_line =+ 1; 130 c = *p_line; 131 p_line++; 132 switch (c) { 133 case 's': 134 testklt = 0; 135 /* 136 turn on 'silent' flag. 137 */ 138 silent = TRUE; 139 break; 140 case 'r': 141 p_line = getval(p_line,sid); 142 break; 143 case 'y': 144 p_line = getval(p_line,type); 145 break; 146 case 'm': 147 p_line = getval(p_line,name); 148 break; 149 default: 150 inline_err =| UNKDUP_ERR; 151 } 152 /* 153 use 'had' array and determine if the keyletter 154 was given twice. 155 */ 156 if (had[c - 'a']++ && testklt++) 157 inline_err =| UNKDUP_ERR; 158 } 159 else { 160 /* 161 assume file name if no '-' preceeded argument 162 */ 163 p_line = getval(p_line,filelist[num_files]); 164 num_files++; 165 } 166 } 167 /* 168 check if any files were named as arguments 169 */ 170 if (num_files == 0) 171 inline_err =| FILARG_ERR; 172 /* 173 check for error in command line. 174 */ 175 if (inline_err && !silent) { 176 if (inpstd) 177 report(inline_err,savelinep,""); 178 else report(inline_err,"",""); 179 return; /* return to 'main' routine */ 180 } 181 line_sw = 1; /* print command line flag */ 182 /* 183 loop through 'validate' for each file on command line. 184 */ 185 for (j = 0; j < num_files; j++) { 186 /* 187 read a file from 'filelist' and place into 'path'. 188 */ 189 sprintf(path,"%s",filelist[j]); 190 validate(path,sid,type,name); 191 inline_err =| infile_err; 192 /* 193 check for error from 'validate' and call 'report' 194 depending on 'silent' flag. 195 */ 196 if (infile_err && !silent) { 197 if (line_sw && inpstd) { 198 report(infile_err,savelinep,path); 199 line_sw = 0; 200 } 201 else report(infile_err,"",path); 202 } 203 } 204 return; /* return to 'main' routine */ 205 } 206 207 208 /* This function actually does the validation on the named file. 209 * It determines whether the file is an SCCS-file or if the file 210 * exists. It also determines if the values given for type, SID, 211 * and name match those in the named file. An error code is returned 212 * if any mismatch occurs. See VAL(I) for more information. 213 */ 214 validate(c_path,c_sid,c_type,c_name) 215 char *c_path; 216 char *c_sid; 217 char *c_type; 218 char *c_name; 219 { 220 register char *l; 221 int goods,goodt,goodn,hadmflag; 222 223 infile_err = goods = goodt = goodn = hadmflag = 0; 224 sinit(&gpkt,c_path); 225 if (!sccsfile(c_path) || (gpkt.p_iop = fopen(c_path,"r")) == NULL) 226 infile_err =| FILENAM_ERR; 227 else { 228 l = get_line(&gpkt); /* read first line in file */ 229 /* 230 check that it is header line. 231 */ 232 if (*l++ != CTLCHAR || *l++ != HEAD) 233 infile_err =| CORRUPT_ERR; 234 235 else { 236 /* 237 get old file checksum count 238 */ 239 satoi(l,&gpkt.p_ihash); 240 gpkt.p_chash = 0; 241 if (HADR) 242 /* 243 check for invalid or ambiguous SID. 244 */ 245 if (invalid(c_sid)) 246 infile_err =| INVALSID_ERR; 247 /* 248 read delta table checking for errors and/or 249 SID. 250 */ 251 if (do_delt(&gpkt,goods,c_sid)) { 252 fclose(gpkt.p_iop); 253 infile_err =| CORRUPT_ERR; 254 return; 255 } 256 257 read_to(EUSERNAM,&gpkt); 258 259 if (HADY || HADM) { 260 /* 261 read flag section of delta table. 262 */ 263 while ((l = get_line(&gpkt)) && 264 *l++ == CTLCHAR && 265 *l++ == FLAG) { 266 NONBLANK(l); 267 repl(l,'\n','\0'); 268 if (*l == TYPEFLAG) { 269 l =+ 2; 270 if (equal(c_type,l)) 271 goodt++; 272 } 273 else if (*l == MODFLAG) { 274 hadmflag++; 275 l =+ 2; 276 if (equal(c_name,l)) 277 goodn++; 278 } 279 } 280 if (*(--l) != BUSERTXT) { 281 fclose(gpkt.p_iop); 282 infile_err =| CORRUPT_ERR; 283 return; 284 } 285 /* 286 check if 'y' flag matched '-y' arg value. 287 */ 288 if (!goodt && HADY) 289 infile_err =| TYPE_ERR; 290 /* 291 check if 'm' flag matched '-m' arg value. 292 */ 293 if (HADM && !hadmflag) { 294 if (!equal(auxf(sname(c_path),'g'),c_name)) 295 infile_err =| NAME_ERR; 296 } 297 else if (HADM && hadmflag && !goodn) 298 infile_err =| NAME_ERR; 299 } 300 else read_to(BUSERTXT,&gpkt); 301 read_to(EUSERTXT,&gpkt); 302 gpkt.p_chkeof = 1; 303 /* 304 read remainder of file so 'read_mod' 305 can check for corruptness. 306 */ 307 while (read_mod(&gpkt)) 308 ; 309 } 310 fclose(gpkt.p_iop); /* close file pointer */ 311 } 312 return; /* return to 'process' function */ 313 } 314 315 316 /* This function reads the 'delta' line from the named file and stores 317 * the information into the structure 'del'. 318 */ 319 getdel(delp,lp) 320 register struct delent *delp; 321 register char *lp; 322 { 323 NONBLANK(lp); 324 delp->type = *lp++; 325 NONBLANK(lp); 326 delp->osid = lp; 327 BLANK(lp); 328 *lp++ = '\0'; 329 NONBLANK(lp); 330 delp->datetime = lp; 331 BLANK(lp); 332 NONBLANK(lp); 333 BLANK(lp); 334 *lp++ = '\0'; 335 NONBLANK(lp); 336 delp->pgmr = lp; 337 BLANK(lp); 338 *lp++ = '\0'; 339 NONBLANK(lp); 340 delp->serial = lp; 341 BLANK(lp); 342 *lp++ = '\0'; 343 NONBLANK(lp); 344 delp->pred = lp; 345 repl(lp,'\n','\0'); 346 } 347 348 349 /* This function does a read through the named file until it finds 350 * the character sent over as an argument. 351 */ 352 read_to(ch,pkt) 353 register char ch; 354 register struct packet *pkt; 355 { 356 register char *n; 357 while ((n = get_line(pkt)) && 358 !(*n++ == CTLCHAR && *n == ch)) 359 ; 360 return; 361 } 362 363 364 /* This function places into a specified destination characters which 365 * are delimited by either a space, tab or 0. It obtains the char- 366 * acters from a line of characters. 367 */ 368 char *getval(sourcep,destp) 369 register char *sourcep; 370 register char *destp; 371 { 372 while (*sourcep != ' ' && *sourcep != '\t' && *sourcep != '\0') 373 *destp++ = *sourcep++; 374 *destp = 0; 375 return(sourcep); 376 } 377 378 379 /* This function will report the error that occured on the command 380 * line. It will print one diagnostic message for each error that 381 * was found in the named file. 382 */ 383 report(code,inp_line,file) 384 register int code; 385 register char *inp_line; 386 register char *file; 387 { 388 char percent; 389 percent = '%'; /* '%' for -m and/or -y messages */ 390 if (*inp_line) 391 printf("%s\n\n",inp_line); 392 if (code & NAME_ERR) 393 printf(" %s: %cM%c, -m mismatch\n",file,percent,percent); 394 if (code & TYPE_ERR) 395 printf(" %s: %cY%c, -y mismatch\n",file,percent,percent); 396 if (code & NONEXSID_ERR) 397 printf(" %s: SID nonexistent\n",file); 398 if (code & INVALSID_ERR) 399 printf(" %s: SID invalid or ambiguous\n",file); 400 if (code & FILENAM_ERR) 401 printf(" %s: can't open file or file not SCCS\n",file); 402 if (code & CORRUPT_ERR) 403 printf(" %s: corrupted SCCS file\n",file); 404 if (code & UNKDUP_ERR) 405 printf(" %s: Unknown or dupilcate keyletter argument\n",file); 406 if (code & FILARG_ERR) 407 printf(" %s: missing file argument\n",file); 408 return; 409 } 410 411 412 /* This function takes as it's argument the SID inputed and determines 413 * whether or not it is valid (e. g. not ambiguous or illegal). 414 */ 415 invalid(i_sid) 416 register char *i_sid; 417 { 418 register int count; 419 register int digits; 420 count = digits = 0; 421 if (*i_sid == '0' || *i_sid == '.') 422 return (1); 423 i_sid++; 424 digits++; 425 while (*i_sid != '\0') { 426 if (*i_sid++ == '.') { 427 digits = 0; 428 count++; 429 if (*i_sid == '0' || *i_sid == '.') 430 return (1); 431 } 432 digits++; 433 if (digits > 5) 434 return (1); 435 } 436 if (*(--i_sid) == '.' ) 437 return (1); 438 if (count == 1 || count == 3) 439 return (0); 440 return (1); 441 } 442 443 444 /* 445 Routine to read a line into the packet. The main reason for 446 it is to make sure that pkt->p_wrttn gets turned off, 447 and to increment pkt->p_slnno. 448 */ 449 450 char *get_line(pkt) 451 register struct packet *pkt; 452 { 453 register char *n; 454 register char *p; 455 456 if ((n = fgets(pkt->p_line,sizeof(pkt->p_line),pkt->p_iop)) != NULL) { 457 pkt->p_slnno++; 458 for (p = pkt->p_line; *p; ) 459 pkt->p_chash =+ *p++; 460 } 461 else { 462 if (!pkt->p_chkeof) 463 infile_err =| CORRUPT_ERR; 464 if (pkt->do_chksum && (pkt->p_chash ^ pkt->p_ihash)&0xFFFF) 465 infile_err =| CORRUPT_ERR; 466 } 467 return(n); 468 } 469 470 471 /* 472 Does initialization for sccs files and packet. 473 */ 474 475 sinit(pkt,file) 476 register struct packet *pkt; 477 register char *file; 478 { 479 480 zero(pkt,sizeof(*pkt)); 481 copy(file,pkt->p_file); 482 pkt->p_wrttn = 1; 483 pkt->do_chksum = 1; /* turn on checksum check for getline */ 484 } 485 486 487 read_mod(pkt) 488 register struct packet *pkt; 489 { 490 register char *p; 491 int ser; 492 int iord; 493 register struct apply *ap; 494 495 while (get_line(pkt) != NULL) { 496 p = pkt->p_line; 497 if (*p++ != CTLCHAR) 498 continue; 499 else { 500 if (!((iord = *p++) == INS || iord == DEL || iord == END)) { 501 infile_err =| CORRUPT_ERR; 502 return(0); 503 } 504 NONBLANK(p); 505 satoi(p,&ser); 506 if (iord == END) 507 remq(pkt,ser); 508 else if ((ap = &pkt->p_apply[ser])->a_code == APPLY) 509 addq(pkt,ser,iord == INS ? YES : NO,iord,ap->a_reason & USER); 510 else 511 addq(pkt,ser,iord == INS ? NO : NULL,iord,ap->a_reason & USER); 512 } 513 } 514 if (pkt->p_q) 515 infile_err =| CORRUPT_ERR; 516 return(0); 517 } 518 519 520 addq(pkt,ser,keep,iord,user) 521 struct packet *pkt; 522 int ser; 523 int keep; 524 int iord; 525 { 526 register struct queue *cur, *prev, *q; 527 528 for (cur = &pkt->p_q; cur = (prev = cur)->q_next; ) 529 if (cur->q_sernum <= ser) 530 break; 531 if (cur->q_sernum == ser) 532 infile_err =| CORRUPT_ERR; 533 prev->q_next = q = alloc(sizeof(*q)); 534 q->q_next = cur; 535 q->q_sernum = ser; 536 q->q_keep = keep; 537 q->q_iord = iord; 538 q->q_user = user; 539 if (pkt->p_ixuser && (q->q_ixmsg = chkix(q,&pkt->p_q))) 540 ++(pkt->p_ixmsg); 541 else 542 q->q_ixmsg = 0; 543 544 setkeep(pkt); 545 } 546 547 548 remq(pkt,ser) 549 register struct packet *pkt; 550 int ser; 551 { 552 register struct queue *cur, *prev; 553 554 for (cur = &pkt->p_q; cur = (prev = cur)->q_next; ) 555 if (cur->q_sernum == ser) 556 break; 557 if (cur) { 558 if (cur->q_ixmsg) 559 --(pkt->p_ixmsg); 560 prev->q_next = cur->q_next; 561 free(cur); 562 setkeep(pkt); 563 } 564 else 565 infile_err =| CORRUPT_ERR; 566 } 567 568 569 setkeep(pkt) 570 register struct packet *pkt; 571 { 572 register struct queue *q; 573 register struct sid *sp; 574 575 for (q = &pkt->p_q; q = q->q_next; ) 576 if (q->q_keep != NULL) { 577 if ((pkt->p_keep = q->q_keep) == YES) { 578 sp = &pkt->p_idel[q->q_sernum].i_sid; 579 pkt->p_inssid.s_rel = sp->s_rel; 580 pkt->p_inssid.s_lev = sp->s_lev; 581 pkt->p_inssid.s_br = sp->s_br; 582 pkt->p_inssid.s_seq = sp->s_seq; 583 } 584 return; 585 } 586 pkt->p_keep = NO; 587 } 588 589 590 # define apply(qp) ((qp->q_iord == INS && qp->q_keep == YES) || (qp->q_iord == DEL && qp->q_keep == NO)) 591 592 chkix(new,head) 593 register struct queue *new; 594 struct queue *head; 595 { 596 register int retval; 597 register struct queue *cur; 598 int firstins, lastdel; 599 600 if (!apply(new)) 601 return(0); 602 for (cur = head; cur = cur->q_next; ) 603 if (cur->q_user) 604 break; 605 if (!cur) 606 return(0); 607 retval = 0; 608 firstins = 0; 609 lastdel = 0; 610 for (cur = head; cur = cur->q_next; ) { 611 if (apply(cur)) { 612 if (cur->q_iord == DEL) 613 lastdel = cur->q_sernum; 614 else if (firstins == 0) 615 firstins = cur->q_sernum; 616 } 617 else if (cur->q_iord == INS) 618 retval++; 619 } 620 if (retval == 0) { 621 if (lastdel && (new->q_sernum > lastdel)) 622 retval++; 623 if (firstins && (new->q_sernum < firstins)) 624 retval++; 625 } 626 return(retval); 627 } 628 629 630 /* This function reads the delta table entries and checks for the format 631 * as specifed in sccsfile(V). If the format is incorrect, a corrupt 632 * error will be issued by 'val'. This function also checks 633 * if the sid requested is in the file (depending if '-r' was specified). 634 */ 635 do_delt(pkt,goods,d_sid) 636 register struct packet *pkt; 637 register int goods; 638 register char *d_sid; 639 { 640 char *l; 641 642 while(getstats(pkt)) { 643 if ((l = get_line(pkt)) && *l++ != CTLCHAR || *l++ != BDELTAB) 644 return(1); 645 if (HADR && !(infile_err & INVALSID_ERR)) { 646 getdel(&del,l); 647 if (equal(d_sid,del.osid) && del.type == 'D') 648 goods++; 649 } 650 while ((l = get_line(pkt)) != NULL) 651 if (pkt->p_line[0] != CTLCHAR) 652 break; 653 else { 654 switch(pkt->p_line[1]) { 655 case EDELTAB: 656 break; 657 case COMMENTS: 658 case MRNUM: 659 case INCLUDE: 660 case EXCLUDE: 661 case IGNORE: 662 continue; 663 default: 664 return(1); 665 } 666 break; 667 } 668 if (l == NULL || pkt->p_line[0] != CTLCHAR) 669 return(1); 670 } 671 if (pkt->p_line[1] != BUSERNAM) 672 return(1); 673 if (HADR && !goods && !(infile_err & INVALSID_ERR)) 674 infile_err =| NONEXSID_ERR; 675 return(0); 676 } 677 678 679 /* This function reads the stats line from the sccsfile */ 680 getstats(pkt) 681 register struct packet *pkt; 682 { 683 register char *p; 684 p = pkt->p_line; 685 if (get_line(pkt) == NULL || *p++ != CTLCHAR || *p != STATS) 686 return(0); 687 return(1); 688 } 689