#include #include #include #include #define maxtag 16 #define lmax 512 #define bufsize 256*64 #define getch() getchar() char lb[lmax] ; union mix { struct labelnode *pointer; int value; }; struct labelnode { struct labelnode *next; union mix labels ; char text[maxtag]; }; char block[maxtag] ; char *infile,*outfile; int change,verbose,exits,printtext,labels,tags,silent,checking,printchanges; int tagsout,noline,deutsch,include_input; FILE *input,*output; int exitcode; char *pos; int line,column,pass,end; struct labelnode *groups; /* *** next *** */ char next(void) /* get next text position */ { char c; int k; c=*pos++; if (c=='\n') { if (noline&&(lb[0]=='\n')) {} else if (change&&(pass==2)) fputs(lb,output); if (fgets(lb,lmax,input)==0) { end=1; return(0); } noline=0; pos=lb; k=strlen(lb); if (k==0 || lb[k-1]!='\n') { lb[k]='\n'; lb[k+1]=0; } c='\n'; } if (printtext) printf("%c",c); column+=1; return(c); } /* *** various supporting functions *** */ void skip(int l) /* skip l characters */ { int i; if (l>0) for (i=0;i0) if (strncmp(pos,text,l)==0) { skip(l); return(1); } advance(); } while (!end); return(0); } void scanfor(char *text) /* scan till text appears */ { if(!(find(text))) { printf("\n%s missing",text); error(); } } /* *** subroutines for german syntax *** */ void umlaut(char a) { int lbl; char repl[3]; if (deutsch&&(pass==2)) { pos=pos-1; repl[0]='\\'; repl[1]='"'; repl[2]=a; lbl=strlen(lb)+1; if (lbl+3>=lmax) { printf("\nLine buffer overflow"); error(); exit(exitcode); } memmove(pos+2,pos,lbl-(int)(pos-lb)); memmove(pos,repl,3); }; } void szet(void) { int lbl; char repl[5]; if (deutsch&&(pass==2)) { pos=pos-1; repl[0]='{'; repl[1]='\\'; repl[2]='s'; repl[3]='s'; repl[4]='}'; lbl=strlen(lb)+1; if (lbl+5>=lmax) { printf("\nLine buffer overflow"); error(); exit(exitcode); } memmove(pos+4,pos,lbl-(int)(pos-lb)); memmove(pos,repl,5); }; } /* *** label handling commands *** */ struct labelnode *findlast(struct labelnode *r) /* find the last label in a list */ { if (r==0) return(0); while (r->next!=0) { r=r->next; }; return(r); } struct labelnode *findnode(struct labelnode *r,char *label) /* scan a list of labels for label */ { if (r==0) return(0); while (strcmp(label,r->text)!=0) { if (r->next==0) return(0); r=r->next; } return(r); } struct labelnode *findlabel(char *group,char *label) /* find a label in label list, return label */ { struct labelnode *r,*findnode(); if ((r=findnode(groups,group))==0) return(0); r=r->labels.pointer; while (r!=0) { if (strcmp(label,r->text)==0) return(r); r=r->next; } return(0); } struct labelnode *newnode(struct labelnode *r,char *label) /* connects a new label to r */ { struct labelnode *new; new=(struct labelnode *)malloc(sizeof(struct labelnode)); if (new==0) { printf("\nNo more space for labels"); error(); exit(exitcode); }; if (r!=0) r->next=new; new->next=0; new->labels.pointer=0; memmove(new->text,label,16); return(new); } int newvalue(char *group) /* find the next number in group */ { int count=1; struct labelnode *r; if ((r=findnode(groups,group))==0) return(count); r=r->labels.pointer; while (r!=0) { count=r->labels.value; r=r->next; } return(count+1); } void correctlabel(char *group,char *label,char *p1,char *p2) /* remove p1-p2 from text and add corresponding number */ { int n,l,lbl,flag; struct labelnode *r; char string[12]; if (strcmp(group,"block")!=0) { flag=1; if ((r=findlabel(group,label))==0) { printf("\nLabel %s.%s not defined",group,label); error(); n=0; } else n=r->labels.value; } else { flag=0; noline=1; } if (change) { if (flag) sprintf(string,"%d",n); else string[0]=0; l=strlen(string); lbl=strlen(lb)+1; if (lbl+l-(int)(p2-p1)>=lmax) { printf("\nLine buffer overflow"); error(); exit(exitcode); } memmove(p1+l,p2,lbl-(int)(p2-lb)); memmove(p1,string,l); if (printchanges&&flag) printf("\n%s.%s changed to %d",group,label,n); pos+=l-(int)(p2-p1); } } void printlabels(void) /* print all used labels */ { struct labelnode *g,*l; printf("\nUsed Labels and Tags : "); g=groups; while (g!=0) { l=g->labels.pointer; while (l!=0) { printf("\n%s.%s = %d",g->text,l->text,l->labels.value); l=l->next; } g=g->next; } } void outlabels(void) /* print all used labels */ { struct labelnode *g,*l; void outopen(char *) ; outopen(outfile); g=groups; while (g!=0) { l=g->labels.pointer; while (l!=0) { fprintf(output,"#;%s.%s=%d\n",g->text,l->text,l->labels.value); l=l->next; } g=g->next; } fclose(output); } void addlabel(char *group,char *label,int n) /* add a label to the label list, value n */ { struct labelnode *r,*new,dummy,*newnode(),*findnode(); struct labelnode *findlast(),*findlabel(); if (strcmp(group,"block")==0) { /* redefine the default block with label */ if (strlen(label)labels.pointer==0) /* if new group */ { new=newnode(&dummy,label); r->labels.pointer=dummy.next; } else new=newnode(findlast(r->labels.pointer),label); /* append label */ new->labels.value=n; } } /* *** tag commands *** */ int gettag(char *group,char *label,char **p1,char **p2,int *n,int *val) /* read a tag or label, and note start and end of labeltext */ { int br; char *p,*ph; skipblanks(); if (*pos=='!' || *pos=='#') { *p1=pos; if (*pos=='!') next(); *p2=pos; *label=0; *group=0; return(1); } if (*pos=='{') { next(); br=1; } else br=0; p=ph=*p1=pos; if (*pos=='@') next(); while (*pos!='}') { skipalnumbs(); if (*pos!='.') break; ph=pos; next(); *p1=pos; } skipalnum(); *p2=pos; if (((*p2-*p1)>maxtag-1)||((ph-p)>maxtag-2)) return(0); if (ph!=p) { memmove(group,p,(int)(ph-p)); *(group+(int)(ph-p))=0; } else memmove(group,block,maxtag-1); memmove(label,*p1,(int)(*p2-*p1)); *(label+(int)(*p2-*p1))=0; if (*val&&(*pos=='=')) { next(); *n=scannumber(); *val=1; *p2=pos; } else *val=0; if (*pos=='_') { next(); *p2=pos; } if (br) if (*pos!='}') return(0); else next(); return(1); } /* *** subroutines to process special syntax features *** */ void brackets(void) /* process {...} */ { int l,c; l=line; c=column; show("{ "); if(!(find("}"))) { printf("\n{ unbalanced in line %d, column %d",l,c); error(); } else show("} "); } void informula(void) /* process $...$ */ { int l,c; show("+$ "); l=line; c=column; if (!(find("$"))) { printf("\nUnmatched $ in line %d, column %d",l,c); error(); }; if (*pos=='$') { printf("\n$$ not allowed here"); error(); } else show("-$ "); } void formula(void) /* process $$...$$ */ { int l,c; show("+$$ "); l=line; c=column; if (!(find("$")) || *pos++!='$') { printf("\nUnmatched $$ in line %d, column %c",l,c); error(); }; if (*pos=='$') { printf("$$$ is illegal"); error(); } else show("-$$ "); } void comment(void) /* pass a coment %... */ { while (*pos!='\n') { next(); }; } void label(void) /* process a label, i.e note the label #=... or change #: to a number, depending on pass, collect #; and cancel */ { char group[maxtag],label[maxtag],a,*p,*p1,*p2; int n,val; p=pos; a=*pos; next(); if ((a=='=')||(a==':')||(a==';')) { if (a==':') val=0; else val=1; /* =value allowed? */ if(!(gettag(group,label,&p1,&p2,&n,&val))) { printf("\nIllegal Label"); error(); exit(exitcode); }; if (pass==1) if ((a=='=')||(a==';')) { if (!val) n=newvalue(group); /* n was not found in =n */ addlabel(group,label,n); } if (pass==2) { if (strcmp(group,"block")==0) addlabel(group,label,0); if (a==';') { correctlabel("block",label,p-1,pos); noline=1; } else correctlabel(group,label,p-1,pos); } } } /* *** command processing functions *** */ void tag(void) /* process \tag {...} or \tag ... */ { char label[maxtag],group[maxtag],*p1,*p2; int n,val=1; if (tags) { *group='@'; if (!(gettag(group+1,label,&p1,&p2,&n,&val))) { printf("\nIllegal Tag"); error(); exit(exitcode); }; if ((pass==1)&&(*p1!='!')&&(*p1!='#')) { if (!val) n=newvalue(group); /* =n not found */ addlabel(group,label,n); } else if (pass==2) if ((*p1!='!')&&(*p1!='#')) correctlabel(group,label,p1,p2); else correctlabel("block",label,p1,p2); } } void thetag(void) { char label[maxtag],group[maxtag],*p1,*p2; int n,val=0; if (tags) { *group='@'; if (!(gettag(group+1,label,&p1,&p2,&n,&val))) { printf("\nIllegal Tag"); error(); exit(exitcode); }; if (pass==2) if ((*p1!='!') && (*p1!='#')) correctlabel(group,label,p1,p2); else correctlabel("block",label,p1,p2); } } void inopen(char *filename); void doinput (void) { FILE *oldfile; char name[512],*n,*oldinfile; int c,oldline; oldfile=input; oldinfile=infile; oldline=line; next(); n=name; while (*pos!=' ' && *pos!='}' && *pos!='\n') { *n++=next(); } *n++=0; inopen(name); line=0; infile=name; find(""); fclose(input); input=oldfile; pos=lb; noline=1; *pos='\n'; infile=oldinfile; line=oldline; end=0; } void command(void) /* process a command \... */ { char com[50]; int l; putcommand(com,&l); skip(l); if (strcmp(com,"tag")==0) tag(); else if (strcmp(com,"thetag")==0) thetag(); else if (strcmp(com,"input")==0 && include_input) doinput(); } /* *** subroutines to process the file *** */ void advance(void) /* advance pos one character or command */ { char c; c=next(); if (checking) switch(c) { case '\n': { line+=1; column=1; if (verbose) printf(" %d ",line); break; } case '{': { brackets(); break; } case '}': { printf("\nUnmatched }"); error(); break; } case '$': { if (*pos=='$') { next(); if (*pos=='$') { printf("$$$ is illegal"); error(); } else formula(); } else informula(); break; } case '\\': { command(); break; } case '%': { comment(); break; } case '#': { if (labels) label(); break; } case '': case '': { umlaut('o'); break; } case '': case '': { umlaut('a'); break; } case '': case '': { umlaut('u'); break; } case '': case '': { umlaut('O'); break; } case '': case '': { umlaut('A'); break; } case '': case '': { umlaut('U'); break; } case '': case '': case '': { szet(); break; } default: { }; } else switch(c) { case '#': { if (labels) label(); break; } case '\n' : { column=1; line+=1; if (verbose) printf("%d ",line); break; } default: { }; } } /* *** main subroutines *** */ void getparams(int n,char *a[]) /* read parameters from command line */ { char *i; if ((n<3)||(*a[1]!='-')) { printf("\nUsage -vpltsne file1.tex file2.tex"); printf("\nv=verbose, p=print, l=labels, t=tags"); printf("\ns=silent, n=no checking e=don't exit on error"); printf("\no=output tags and labels, d=deutsch, i=include input"); wait(); exit(1); } verbose=printtext=labels=tags=silent=deutsch=include_input=0; exits=checking=1; for (i=a[1]+1;*i!=0;i++) switch(toupper(*i)) { case 'V': { verbose=1; break; } case 'P': { printtext=1; verbose=0; break; } case 'L': { labels=1; break; } case 'T': { tags=1; break; } case 'S': { silent=1; break; } case 'N': { checking=0; break; } case 'E': { exits=0; break; } case 'O': { tagsout=1; break; } case 'D': { deutsch=1; break; } case 'I': { include_input=1; break; } default: { } } infile=a[2]; if (n<4) change=0; else { outfile=a[3]; change=1; }; if (tagsout) change=0 ; } void inopen(char *filename) { char name[512]; if ((input=fopen(filename,"r"))==0) { strcpy(name,filename); strcat(name,".tex"); if ((input=fopen(name,"r"))==0) { printf("\nUnable to open %s",filename); wait(); exit(1); } }; setvbuf(input,0,_IOFBF,bufsize); lb[0]='\n'; pos=lb; noline=1; } void outopen(char *filename) { if ((output=fopen(filename,"w"))==0) { printf("\nUnable to open %s",filename); wait(); exit(1); }; setvbuf(output,0,_IOFBF,bufsize); } void checkfile(void) /* check the syntax of the file at filestart */ { printf("\nPass 1 : \n"); /* line swap on */ line=0; column=1; groups=0; block[0]=0; pass=1; end=0; inopen(infile); if (labels||tags) find(""); if ((labels||tags)&&(!silent)) printlabels(); if (tagsout) outlabels() ; else { pass=2; line=0; column=1; printtext=0; printchanges=verbose; verbose=0; end=0; block[0]=0; rewind(input); lb[0]='\n'; pos=lb; noline=1; if (change) outopen(outfile); printf("\nPass 2 : \n"); find(""); if (change) fclose(output) ; } printlast(); fclose(input) ; printf("\n\n"); } main(int argc,char *argv[]) /* main function to read in, process and write file */ { printf( "\n ***** Checker, Copyright by R. Grothmann, Version %s ***** \n", __DATE__); getparams(argc,argv); if (change) printf("\nModifying %s to %s.",infile,outfile); else printf("\nChecking %s.",infile); checkfile(); return(exitcode); }