/************************************************************************/ /* */ /* val - */ /* val [-mname] [-rSID] [-s] [-ytype] file ... */ /* */ /************************************************************************/ # include "../hdr/defines.h" # include "../hdr/had.h" # define FILARG_ERR 0200 /* no file name given */ # define UNKDUP_ERR 0100 /* unknown or duplicate keyletter */ # define CORRUPT_ERR 040 /* corrupt file error code */ # define FILENAM_ERR 020 /* file name error code */ # define INVALSID_ERR 010 /* invalid or ambiguous SID error */ # define NONEXSID_ERR 04 /* non-existent SID error code */ # define TYPE_ERR 02 /* type arg value error code */ # define NAME_ERR 01 /* name arg value error code */ # define TRUE 1 # define FALSE 0 # define BLANK(l) while (!(*l == ' ' || *l == '\t')) l++; int ret_code; /* prime return code from 'main' program */ int inline_err; /* input line error code (from 'process') */ int infile_err; /* file error code (from 'validate') */ int inpstd; /* TRUE = args from standard input */ struct packet gpkt; char had[26]; /* had flag used in 'process' function */ char path[50]; /* storage for file name value */ char sid[50]; /* storage for sid (-r) value */ char type[50]; /* storage for type (-y) value */ char name[50]; /* storage for name (-m) value */ char line[BUFSIZ]; char *get_line(); /* function returning ptr to line read */ char *getval(); /* function returning adjusted ptr to line */ char *alloc(); /* function returning ptr */ char *fgets(); /* function returning i/o ptr */ struct delent { /* structure for delta table entry */ char type; char *osid; char *datetime; char *pgmr; char *serial; char *pred; } del; SCCSID(@(#)val.c 4.2); /* This is the main program that determines whether the command line * comes from the standard input or read off the original command * line. See VAL(I) for more information. */ main(argc,argv) int argc; char *argv[]; { FILE *iop; register int j; ret_code = 0; if (argc == 2 && argv[1][0] == '-' && !(argv[1][1])) { inpstd = TRUE; iop = stdin; /* read from standard input */ while (fgets(line,BUFSIZ,iop) != NULL) { if (line[0] != '\n') { repl (line,'\n','\0'); process(line); ret_code =| inline_err; } } } else { inpstd = FALSE; for (j = 1; j < argc; j++) sprintf(&(line[strlen(line)]),"%s ",argv[j]); j = strlen(line) - 1; line[j > 0 ? j : 0] = NULL; process(line); ret_code = inline_err; } exit(ret_code); } /* This function processes the line sent by the main routine. It * determines which keyletter values are present on the command * line and assigns the values to the correct storage place. It * then calls validate for each file name on the command line * It will return to main if the input line contains an error, * otherwise it returns any error code found by validate. */ process(p_line) char *p_line; { register int j; register int testklt; register int line_sw; int silent; int num_files; char filelist[50][50]; char *savelinep; char c; silent = FALSE; path[0] = sid[0] = type[0] = name[0] = 0; num_files = inline_err = 0; /* make copy of 'line' for use later */ savelinep = p_line; /* clear out had flags for each 'line' processed */ for (j = 0; j < 27; j++) had[j] = 0; /* execute loop until all characters in 'line' are checked. */ while (*p_line) { testklt = 1; NONBLANK(p_line); if (*p_line == '-') { p_line =+ 1; c = *p_line; p_line++; switch (c) { case 's': testklt = 0; /* turn on 'silent' flag. */ silent = TRUE; break; case 'r': p_line = getval(p_line,sid); break; case 'y': p_line = getval(p_line,type); break; case 'm': p_line = getval(p_line,name); break; default: inline_err =| UNKDUP_ERR; } /* use 'had' array and determine if the keyletter was given twice. */ if (had[c - 'a']++ && testklt++) inline_err =| UNKDUP_ERR; } else { /* assume file name if no '-' preceeded argument */ p_line = getval(p_line,filelist[num_files]); num_files++; } } /* check if any files were named as arguments */ if (num_files == 0) inline_err =| FILARG_ERR; /* check for error in command line. */ if (inline_err && !silent) { if (inpstd) report(inline_err,savelinep,""); else report(inline_err,"",""); return; /* return to 'main' routine */ } line_sw = 1; /* print command line flag */ /* loop through 'validate' for each file on command line. */ for (j = 0; j < num_files; j++) { /* read a file from 'filelist' and place into 'path'. */ sprintf(path,"%s",filelist[j]); validate(path,sid,type,name); inline_err =| infile_err; /* check for error from 'validate' and call 'report' depending on 'silent' flag. */ if (infile_err && !silent) { if (line_sw && inpstd) { report(infile_err,savelinep,path); line_sw = 0; } else report(infile_err,"",path); } } return; /* return to 'main' routine */ } /* This function actually does the validation on the named file. * It determines whether the file is an SCCS-file or if the file * exists. It also determines if the values given for type, SID, * and name match those in the named file. An error code is returned * if any mismatch occurs. See VAL(I) for more information. */ validate(c_path,c_sid,c_type,c_name) char *c_path; char *c_sid; char *c_type; char *c_name; { register char *l; int goods,goodt,goodn,hadmflag; infile_err = goods = goodt = goodn = hadmflag = 0; sinit(&gpkt,c_path); if (!sccsfile(c_path) || (gpkt.p_iop = fopen(c_path,"r")) == NULL) infile_err =| FILENAM_ERR; else { l = get_line(&gpkt); /* read first line in file */ /* check that it is header line. */ if (*l++ != CTLCHAR || *l++ != HEAD) infile_err =| CORRUPT_ERR; else { /* get old file checksum count */ satoi(l,&gpkt.p_ihash); gpkt.p_chash = 0; if (HADR) /* check for invalid or ambiguous SID. */ if (invalid(c_sid)) infile_err =| INVALSID_ERR; /* read delta table checking for errors and/or SID. */ if (do_delt(&gpkt,goods,c_sid)) { fclose(gpkt.p_iop); infile_err =| CORRUPT_ERR; return; } read_to(EUSERNAM,&gpkt); if (HADY || HADM) { /* read flag section of delta table. */ while ((l = get_line(&gpkt)) && *l++ == CTLCHAR && *l++ == FLAG) { NONBLANK(l); repl(l,'\n','\0'); if (*l == TYPEFLAG) { l =+ 2; if (equal(c_type,l)) goodt++; } else if (*l == MODFLAG) { hadmflag++; l =+ 2; if (equal(c_name,l)) goodn++; } } if (*(--l) != BUSERTXT) { fclose(gpkt.p_iop); infile_err =| CORRUPT_ERR; return; } /* check if 'y' flag matched '-y' arg value. */ if (!goodt && HADY) infile_err =| TYPE_ERR; /* check if 'm' flag matched '-m' arg value. */ if (HADM && !hadmflag) { if (!equal(auxf(sname(c_path),'g'),c_name)) infile_err =| NAME_ERR; } else if (HADM && hadmflag && !goodn) infile_err =| NAME_ERR; } else read_to(BUSERTXT,&gpkt); read_to(EUSERTXT,&gpkt); gpkt.p_chkeof = 1; /* read remainder of file so 'read_mod' can check for corruptness. */ while (read_mod(&gpkt)) ; } fclose(gpkt.p_iop); /* close file pointer */ } return; /* return to 'process' function */ } /* This function reads the 'delta' line from the named file and stores * the information into the structure 'del'. */ getdel(delp,lp) register struct delent *delp; register char *lp; { NONBLANK(lp); delp->type = *lp++; NONBLANK(lp); delp->osid = lp; BLANK(lp); *lp++ = '\0'; NONBLANK(lp); delp->datetime = lp; BLANK(lp); NONBLANK(lp); BLANK(lp); *lp++ = '\0'; NONBLANK(lp); delp->pgmr = lp; BLANK(lp); *lp++ = '\0'; NONBLANK(lp); delp->serial = lp; BLANK(lp); *lp++ = '\0'; NONBLANK(lp); delp->pred = lp; repl(lp,'\n','\0'); } /* This function does a read through the named file until it finds * the character sent over as an argument. */ read_to(ch,pkt) register char ch; register struct packet *pkt; { register char *n; while ((n = get_line(pkt)) && !(*n++ == CTLCHAR && *n == ch)) ; return; } /* This function places into a specified destination characters which * are delimited by either a space, tab or 0. It obtains the char- * acters from a line of characters. */ char *getval(sourcep,destp) register char *sourcep; register char *destp; { while (*sourcep != ' ' && *sourcep != '\t' && *sourcep != '\0') *destp++ = *sourcep++; *destp = 0; return(sourcep); } /* This function will report the error that occured on the command * line. It will print one diagnostic message for each error that * was found in the named file. */ report(code,inp_line,file) register int code; register char *inp_line; register char *file; { char percent; percent = '%'; /* '%' for -m and/or -y messages */ if (*inp_line) printf("%s\n\n",inp_line); if (code & NAME_ERR) printf(" %s: %cM%c, -m mismatch\n",file,percent,percent); if (code & TYPE_ERR) printf(" %s: %cY%c, -y mismatch\n",file,percent,percent); if (code & NONEXSID_ERR) printf(" %s: SID nonexistent\n",file); if (code & INVALSID_ERR) printf(" %s: SID invalid or ambiguous\n",file); if (code & FILENAM_ERR) printf(" %s: can't open file or file not SCCS\n",file); if (code & CORRUPT_ERR) printf(" %s: corrupted SCCS file\n",file); if (code & UNKDUP_ERR) printf(" %s: Unknown or dupilcate keyletter argument\n",file); if (code & FILARG_ERR) printf(" %s: missing file argument\n",file); return; } /* This function takes as it's argument the SID inputed and determines * whether or not it is valid (e. g. not ambiguous or illegal). */ invalid(i_sid) register char *i_sid; { register int count; register int digits; count = digits = 0; if (*i_sid == '0' || *i_sid == '.') return (1); i_sid++; digits++; while (*i_sid != '\0') { if (*i_sid++ == '.') { digits = 0; count++; if (*i_sid == '0' || *i_sid == '.') return (1); } digits++; if (digits > 5) return (1); } if (*(--i_sid) == '.' ) return (1); if (count == 1 || count == 3) return (0); return (1); } /* Routine to read a line into the packet. The main reason for it is to make sure that pkt->p_wrttn gets turned off, and to increment pkt->p_slnno. */ char *get_line(pkt) register struct packet *pkt; { register char *n; register char *p; if ((n = fgets(pkt->p_line,sizeof(pkt->p_line),pkt->p_iop)) != NULL) { pkt->p_slnno++; for (p = pkt->p_line; *p; ) pkt->p_chash =+ *p++; } else { if (!pkt->p_chkeof) infile_err =| CORRUPT_ERR; if (pkt->do_chksum && (pkt->p_chash ^ pkt->p_ihash)&0xFFFF) infile_err =| CORRUPT_ERR; } return(n); } /* Does initialization for sccs files and packet. */ sinit(pkt,file) register struct packet *pkt; register char *file; { bzero(pkt,sizeof(*pkt)); copy(file,pkt->p_file); pkt->p_wrttn = 1; pkt->do_chksum = 1; /* turn on checksum check for getline */ } read_mod(pkt) register struct packet *pkt; { register char *p; int ser; int iord; register struct apply *ap; while (get_line(pkt) != NULL) { p = pkt->p_line; if (*p++ != CTLCHAR) continue; else { if (!((iord = *p++) == INS || iord == DEL || iord == END)) { infile_err =| CORRUPT_ERR; return(0); } NONBLANK(p); satoi(p,&ser); if (iord == END) remq(pkt,ser); else if ((ap = &pkt->p_apply[ser])->a_code == APPLY) addq(pkt,ser,iord == INS ? YES : NO,iord,ap->a_reason & USER); else addq(pkt,ser,iord == INS ? NO : NULL,iord,ap->a_reason & USER); } } if (pkt->p_q) infile_err =| CORRUPT_ERR; return(0); } addq(pkt,ser,keep,iord,user) struct packet *pkt; int ser; int keep; int iord; { register struct queue *cur, *prev, *q; for (cur = &pkt->p_q; cur = (prev = cur)->q_next; ) if (cur->q_sernum <= ser) break; if (cur->q_sernum == ser) infile_err =| CORRUPT_ERR; prev->q_next = q = alloc(sizeof(*q)); q->q_next = cur; q->q_sernum = ser; q->q_keep = keep; q->q_iord = iord; q->q_user = user; if (pkt->p_ixuser && (q->q_ixmsg = chkix(q,&pkt->p_q))) ++(pkt->p_ixmsg); else q->q_ixmsg = 0; setkeep(pkt); } remq(pkt,ser) register struct packet *pkt; int ser; { register struct queue *cur, *prev; for (cur = &pkt->p_q; cur = (prev = cur)->q_next; ) if (cur->q_sernum == ser) break; if (cur) { if (cur->q_ixmsg) --(pkt->p_ixmsg); prev->q_next = cur->q_next; free(cur); setkeep(pkt); } else infile_err =| CORRUPT_ERR; } setkeep(pkt) register struct packet *pkt; { register struct queue *q; register struct sid *sp; for (q = &pkt->p_q; q = q->q_next; ) if (q->q_keep != NULL) { if ((pkt->p_keep = q->q_keep) == YES) { sp = &pkt->p_idel[q->q_sernum].i_sid; pkt->p_inssid.s_rel = sp->s_rel; pkt->p_inssid.s_lev = sp->s_lev; pkt->p_inssid.s_br = sp->s_br; pkt->p_inssid.s_seq = sp->s_seq; } return; } pkt->p_keep = NO; } # define apply(qp) ((qp->q_iord == INS && qp->q_keep == YES) || (qp->q_iord == DEL && qp->q_keep == NO)) chkix(new,head) register struct queue *new; struct queue *head; { register int retval; register struct queue *cur; int firstins, lastdel; if (!apply(new)) return(0); for (cur = head; cur = cur->q_next; ) if (cur->q_user) break; if (!cur) return(0); retval = 0; firstins = 0; lastdel = 0; for (cur = head; cur = cur->q_next; ) { if (apply(cur)) { if (cur->q_iord == DEL) lastdel = cur->q_sernum; else if (firstins == 0) firstins = cur->q_sernum; } else if (cur->q_iord == INS) retval++; } if (retval == 0) { if (lastdel && (new->q_sernum > lastdel)) retval++; if (firstins && (new->q_sernum < firstins)) retval++; } return(retval); } /* This function reads the delta table entries and checks for the format * as specifed in sccsfile(V). If the format is incorrect, a corrupt * error will be issued by 'val'. This function also checks * if the sid requested is in the file (depending if '-r' was specified). */ do_delt(pkt,goods,d_sid) register struct packet *pkt; register int goods; register char *d_sid; { char *l; while(getstats(pkt)) { if ((l = get_line(pkt)) && *l++ != CTLCHAR || *l++ != BDELTAB) return(1); if (HADR && !(infile_err & INVALSID_ERR)) { getdel(&del,l); if (equal(d_sid,del.osid) && del.type == 'D') goods++; } while ((l = get_line(pkt)) != NULL) if (pkt->p_line[0] != CTLCHAR) break; else { switch(pkt->p_line[1]) { case EDELTAB: break; case COMMENTS: case MRNUM: case INCLUDE: case EXCLUDE: case IGNORE: continue; default: return(1); } break; } if (l == NULL || pkt->p_line[0] != CTLCHAR) return(1); } if (pkt->p_line[1] != BUSERNAM) return(1); if (HADR && !goods && !(infile_err & INVALSID_ERR)) infile_err =| NONEXSID_ERR; return(0); } /* This function reads the stats line from the sccsfile */ getstats(pkt) register struct packet *pkt; { register char *p; p = pkt->p_line; if (get_line(pkt) == NULL || *p++ != CTLCHAR || *p != STATS) return(0); return(1); }