diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/relabel/relabelmac.pas |
Initial commit
Diffstat (limited to 'support/relabel/relabelmac.pas')
-rw-r--r-- | support/relabel/relabelmac.pas | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/support/relabel/relabelmac.pas b/support/relabel/relabelmac.pas new file mode 100644 index 0000000000..f5b671181e --- /dev/null +++ b/support/relabel/relabelmac.pas @@ -0,0 +1,503 @@ +PROGRAM relabelmac; {For Turbo Pascal for Macintosh.} +{Peter Ungar, 914 723 7187, June 7, 1994; Mar. 5th, Mar. 31th 1995} +{Change of Mar. 5, 1995: In case of a reference to a nonexistent label, +RELABEL is supposed to print out the nonexistent label. Before this +correction this failed to work in most cases. +Change of Mar. 31: The messages to input the numbers of the first +chapter and the first section were changed. The old messages said -1 was +allowed, but RELABEL is programmed to regard the symbol - as +not part of a label. + Also, the error message for nonexistent labels was made clearer.} + +LABEL 1,3,8,9; + +TYPE stringarray=array[1..6] of string[12]; + + (* LaTeX calls the identifier "label" when it occurs next to + the item, and "reference" elsewhere. I apologize for not having + observed this distinction. I should have used "identifier" for + an occurrence of a string which could be a label or a reference, + but for the most part I called it a label. In text written + for RELABEL, as in typeset books, labels and references + are not marked as such by the author. RELABEL regards the first + occurrence of an identifier as the label, unless it is marked as a + forward reference. + The program makes two passes over the text. The first + involves reading only. The program takes down each label at its + first nonforward occurrence and assigns to it the next + available serial number. These pairs of old and new labels are + stored in the labelentry file. Next, the text is read again and + written out to a new file, with each label changed to its new + value. + + Stringarrays will hold labels, including those parts of the label + which are implicit, i.e. if we omit the chapter label or the + chapter and section label or the chapter, section and subsection + label when those are the current ones. + The entries of the stringarray are: + a) If SectionReset=TRUE: chapterlabel, sectionlabel, + subsection label and the identifier of the object of the label. + If the item does not apply, e.g. if we are not in a subsection, + the corresponding entry is the empty string ''; + b) If SectionReset=FALSE, i.e. sections are numbered + consecutively, then chapter numbers are not needed in labels and + the first entry of the string array is empty except in the + label of a chapter. *) + + (* We use the following pointer construction to get around the fact + that Turbo Pascal allots a space of only 32K to + all regularly declared variables, but can utilize memory outside + that limited space for variables created by NEW statements. *) + + labelentry=RECORD oldlabel:stringarray; + newlabel:array[1..4] of integer; + secondpassoccurred:boolean; + END; + labelentryp=^labelentry; + sublistp=array[-1..2000] of labelentryp; + mainlistp=^sublistp; + + (* Oldlabel is a stringarray. The labels in the old text may + contain lowercase letters as well as digits. + Newlabel is represented by an array of integers, the + chapter number etc. This will be made into a string when there is + a reference to the label.*) + + VAR i,j,k,n,implicit,ImplAdj,SerialNo, + FirstChapter,FirstSection,LabelKind:integer; + (* ImplAdj (ImplicitAdjustment) is 0 if section numbers are reset + at the beginning of each chapter and 1 if they are not, and + hence chapter numbers are not needed in labels other than chapter + labels. + SerialNo will hold the serial number of the first occurrence + of a label in the array holding all labels of its kind. + FirstChapter, FirstSection are the numbers to be assigned to the + first chapter and the first section of the file. RELABEL asks an + input of this when the program starts, in case you want 0, 1 or + larger numbers if the file is from the middle of a manuscript. *) + totalcount, currentcount:array[0..9] of integer; + (* countstarts gives where the numbering of chapters, sections + subsections and other labeled items begins; the program sets + the last two equal to 1. *) + s,filename,outputfilename, filenameend:string; + labelstring:string[51]; + sta,empty,current:stringarray; + + (* totalcount will say how many labels of the 10 kinds we have + so far. currentcount gives the counts of the 10 kinds since they + were last reset. + current contains the current chapterlabel, section + label and subsection label. *) + + mainlist:array[0..9] of mainlistp; + SectionReset,ForwardRef, LabeltagsInOutput, omit, + PeriodFollows, FirstChapterLabel, FirstSectionLabel: boolean; +(* Sectionreset: Each chapter starts with section 1.*) + + incoming, outgoing:text; + c,ans:char; + +FUNCTION min(i,j:integer):integer; +BEGIN + IF i<j THEN min:=i ELSE min:=j +END; + +FUNCTION max(i,j:integer):integer; +BEGIN + IF i<j THEN max:=j ELSE max:=i +END; + +PROCEDURE IntToStr(a:integer; VAR s:string); + (*Writes a as a string s*) + LABEL 9; + VAR d:integer; + BEGIN + s:=''; + IF a<0 THEN BEGIN s:='-'; a:=-a END; + IF a=0 THEN BEGIN s:='0'; GOTO 9 END; + d:=10000; + WHILE (a DIV d)=0 DO d:=d DIV 10; + WHILE d>0 DO BEGIN s:=s+chr(48+(a DIV d)); a:= a MOD d; d:=d DIV 10 END; + 9:END; + + FUNCTION labelsymbol(c:char):boolean; +(* TRUE if c is a symbol allowed in a label, i.e. a digit or + a lowercase letter. *) + VAR x:integer; + BEGIN x:=ord(c); + labelsymbol:=(((48<=x) AND (x<=57)) OR ((97<=x) AND (x<=122)) + OR ((65<=x) AND (x<=90))) + END; + + PROCEDURE ReadLabel(VAR star:stringarray; VAR c:char); + (* Reads the components of a label from the incoming stream and + puts its components into the stringarray star, including + the implicit items. If section numbers are not reset in each + chapter (2 levels of test units) then the empty string is put in + the chapter designator location star[1] except in the case of + chapter labels. *) + LABEL 2,5,9; + VAR i,implicit:integer; str:string[12]; + BEGIN + k:=0; star:=empty; PeriodFollows:=FALSE; + (* k will count the number of parts separated by .'s in the label. *) + 2: str:=''; + WHILE labelsymbol(c) DO BEGIN + str:=str+c; + read(incoming,c); + END; + k:=k+1; star[k+ImplAdj]:=str; + IF c='.' THEN BEGIN + (* Another part of the label is coming, or there is a . after + the label. *) + read(incoming,c); + IF labelsymbol(c) THEN GOTO 2 + ELSE PeriodFollows:=TRUE; + END; + (* Now we have all k items of the label in the stringarray star. + We want the program to work so that, if desired, the chapter, section + and subsection labels may be omitted and then they are understood to + refer to the current chapter, section or subsection. + We reconstruct the omitted parts of the label. We assemble it + in star and then if it is a new label we put it into the mainlist. + If the number of levels is 3, then the + components of star are: Chapter label, Section label, Subsection + label and item label. If any of these are missing or not applicable, + the corresponding label is ''. A subsection label is missing + if the section is not divided into subsections. A subsection + label is inapplicable if the label is a chapter or section label. + If the number of levels is 2, (ImplAdj=1) then a label of + anything except a chapter is complete without the chapter label, + and star[1]:='' for all such items. *) + IF (LabelKind > 1) AND ((k+ImplAdj) > min(LabelKind, 4)) THEN BEGIN + writeln('Found a label with too many parts. If you have 2 levels,'); + writeln('only a chapter label should contain a chapter identifier.'); + GOTO 5; + END; + IF ((LabelKind=0) AND (k>1)) THEN BEGIN + writeln(' An at-large item label with more than one part: '); +5: write('LabelKind: ',Labelkind,' Label: '); + FOR i:=1+ImplAdj TO k+ImplAdj-1 DO write(sta[i],'.'); + writeln(sta[k+ImplAdj]); + writeln('Continue? y or n '); readln(ans); + IF ans='n' THEN BEGIN close(incoming); halt END; + END; (* Of error handling. *) + IF LabelKind=0 THEN BEGIN + star[4]:=star[1+ImplAdj]; star[1+ImplAdj]:=''; + END; + IF LabelKind=1 THEN BEGIN + star[1]:=star[1+ImplAdj]; star[2]:='';GOTO 9 END; + IF (2<=LabelKind) AND (LabelKind<=3) THEN BEGIN + implicit:=LabelKind-k-ImplAdj; + IF implicit>0 THEN BEGIN + FOR i:=LabelKind DOWNTO implicit+1 DO star[i]:=star[i-implicit]; + FOR i:=1+ImplAdj TO implicit+ImplAdj DO star[i]:=current[i]; + END; + END; + IF (labelkind >= 4) THEN BEGIN + IF k+ImplAdj<4 THEN BEGIN + star[4]:=star[k+ImplAdj]; star[k+ImplAdj]:='' END; + IF k=1 THEN FOR i:=1+ImplAdj to 3 DO star[i]:=current[i]; + (*This must be a label of an item in the current text unit.*) + END; +9:END; (* of procedure readlabel. Note that c is the first symbol after + the label at this stage, or the first symbol after the period + if there was a period immediately after the label. *) + + FUNCTION InList(st:StringArray;VAR SerialNo:integer):boolean; + (* TRUE if the label st is already in the mainlist. + This function is also used to compute the index SerialNo of + the label in the array of that LabelKind*) + LABEL 9; + VAR i:integer; bool:boolean; + BEGIN + IF totalcount[LabelKind]=0 THEN InList:=FALSE + ELSE FOR i:=totalcount[LabelKind] DOWNTO 1 DO + BEGIN + bool:=TRUE; + WITH mainlist[LabelKind]^[i]^ DO + FOR j:=1 TO 4 DO bool:=bool AND (oldlabel[j]=st[j]); + IF bool THEN BEGIN + InList:=TRUE; + SerialNo:=i; + (* mainlist[LabelKind]^[SerialNo] is the record where this + label and the new label which is to replace it can be found.*) + GOTO 9 END; + END; + InList:=FALSE; (* The label is not in mainlist. *) +9:END; + + + PROCEDURE AddToList(sta:stringarray); +(* We have a new label which is not a forward reference. Add it to mainlist*) + VAR i:integer; + BEGIN + totalcount[LabelKind]:=totalcount[LabelKind]+1; + currentcount[LabelKind]:=currentcount[LabelKind]+1; + (* If the new label is a chapter, section or subsection label then + the counts of subordinate items have to be reset.*) + + IF (0<LabelKind) AND (LabelKind<=3) THEN BEGIN + current[labelkind]:=sta[labelkind]; + FOR i:=max(labelkind+1,ImplAdj+2) TO 9 DO BEGIN + currentcount[i]:=0; current[i]:=''; + END; + IF (FirstChapterLabel AND (labelkind=1)) THEN BEGIN + CurrentCount[1]:=FirstChapter; FirstChapterLabel:=False + END; + IF (FirstSectionLabel AND (labelkind=2)) THEN BEGIN + CurrentCount[2]:=FirstSection; FirstSectionLabel:=False + END; + END; (* of adjusting the label counts and the array of current chapter, + section and subsection to account for the new label. + Next add a new blank record where we will put the label we just found, + and the label which will replace it. *) + + new(mainlist[LabelKind]^[totalcount[LabelKind]]); + WITH mainlist[LabelKind]^[totalcount[LabelKind]]^ DO BEGIN + oldlabel:=sta; + FOR i:=1 to 4 DO newlabel[i]:=0; + IF LabelKind=0 THEN newlabel[4]:= currentcount[0]; + IF LabelKind=1 THEN newlabel[1]:=currentcount[1]; + IF LabelKind>=2 THEN + FOR i:=1+ImplAdj TO min(3,LabelKind) DO newlabel[i]:=currentcount[i]; + IF LabelKind>=4 THEN newlabel[4]:=currentcount[labelkind]; + (* Newlabel[1] is 0 if sections are numbered + consecutively and chapter numbers occur only in chapter labels.*) + SecondPassOccurred:=FALSE; + END; (* of preparing new mainlist entry *) + END; (* of processing the new label *) + + PROCEDURE MakeNewLabelstring(VAR newlabelstring:string; + labelkind,SerialNo:integer); + LABEL 9; + VAR j:integer;s:string; + InUnitRef:boolean; + BEGIN + WITH mainlist[LabelKind]^[SerialNo]^ DO + BEGIN + newlabelstring:=''; + IF LabelKind=0 THEN + BEGIN IntToStr(newlabel[4],newlabelstring); + GOTO 9 END; (* Of LabelKind=0 (at large label) case *) + IF LabelKind=1 THEN + BEGIN IntToStr(newlabel[1],newlabelstring); + GOTO 9 END; (* Of LabelKind=1 (i.e. chapterlabel) case *) + + IF omit AND (LabelKind>3) THEN BEGIN + (* Find whether the reference is to the current unit of the text.*) + InUnitRef:=TRUE; + FOR j:=1+ImplAdj TO 3 DO + InUnitRef:=InUnitRef AND (newlabel[j]=currentcount[j]); + IF InUnitRef THEN BEGIN + IntToStr(newlabel[4],newlabelstring); GOTO 9 END; + END; (* Of making label without chapter,section and subsection + number. Note we exit to 9 only if such a label has been made. *) + FOR j:=1+ImplAdj TO min(LabelKind,4) DO + IF OldLabel[j] <> '' THEN BEGIN + IntToStr(newlabel[j],s); + IF newlabelstring='' THEN newlabelstring:=s + ELSE newlabelstring:=newlabelstring+'.'+s; + END; + END; (* of WITH statement *) +9:END; + + FUNCTION LabelTag(LabelKind:integer):char; (* For error message.*) + BEGIN + CASE LabelKind OF + 0: LabelTag:= ''; {Option a, At large item ASCII 140} + 1: LabelTag:= ''; {Option c, Chapter ASCII 141} + 2: LabelTag:= ''; {Option s, Section ASCII 167} + 3: LabelTag:= ''; {Option b, suBsection ASCII 186} + 4: LabelTag:= ''; {Option g, fiGure ASCII 169} + 5: LabelTag:= ''; {Option f, Formula ASCII 196} + 6: LabelTag:= ''; {Option p, Problem ASCII 185} + 7: LabelTag:= ''; {Option d, Definition ASCII 182} + 8: LabelTag:= ''; {Option t, Theorem ASCII 160} + 9: LabelTag:= ''; {Option l, Lemma ASCII 194} + (* In the IBM-compatible version of RELABEL different label tags are used. + Their ASCII numbers are 227, 228, 229, 221, 222,..., 226 *) + END; + END; (* Main program begins here.*) +BEGIN +1: FOR i:=1 TO 4 DO empty[i]:=''; current:=empty; + writeln('File to be renumbered. If it is not in the same folder as RENUMBER'); + writeln('then give path name, e.g. HardDisk:Calculus Book Folder:Ch3 '); + readln(filename); + writeln('Do you have a 2 level (Section, possibly subsection) or'); + writeln('3 level (Chapter, section, subsection) hierarchy of text units?'); + write('Input 2 or 3 '); + readln(i); SectionReset:= i=3; ImplAdj:=3-i; + (*ImplAdj (Implicit Adjustment) = 1 if chapter numbers are superfluous + in labels, 0 otherwise.*) + writeln('Do you want the chapter, section and subsection numbers'); + writeln('in the new file if they are all the current ones (y or n) '); + writeln('(Chapter numbers are included only if you have 3 levels)'); + readln(ans); omit:=ans='n'; + writeln('Do you want the label tags to remain in the output? y or n: '); + readln(ans); LabeltagsInOutput:=ans='y'; + IF labeltagsinoutput THEN filenameend:='R' ELSE filenameend:='T'; + FOR i:=0 TO 9 DO currentcount[i]:=0; + TotalCount:=CurrentCount; + writeln('Input the number >= 0 you want for the first chapter'); + readln(FirstChapter); + currentcount[1]:=FirstChapter-1; FirstChapterLabel:=TRUE; + writeln('Input the number >= 0 you want for the first section'); + readln(FirstSection); + currentcount[2]:=FirstSection-1; FirstSectionLabel:=TRUE; + writeln('I will beep when ready'); + (* Next create the arrays for storing the various types of labels. *) + + FOR i:=0 TO 9 DO new(mainlist[i]); + + Reset(incoming,filename{,50000}); (* Use large buffer to spare disk drive *) + read(incoming, c); + WHILE eof(incoming)=FALSE DO + BEGIN + IF ord(c) > 128 THEN BEGIN + CASE c OF + '': LabelKind:=0; + '': LabelKind:=1; + '': LabelKind:=2; + '': LabelKind:=3; + '': LabelKind:=4; + '': LabelKind:=5; + '': LabelKind:=6; + '': LabelKind:=7; + '': LabelKind:=8; + '': LabelKind:=9; + OTHERWISE (* In case the file comes from MS-DOS user of RELABEL *) + IF ((227 <= ord(c)) AND (ord(c)<= 229)) + THEN LabelKind:= ord(c)-226 + ELSE IF ((221 <= ord(c)) AND (ord(c)<= 226)) + THEN LabelKind:= ord(c)-217 + ELSE IF ord(c)=220 THEN LabelKind:=0 + ELSE GOTO 8; + END; + read(incoming, c); + IF (c<>'') THEN readlabel(sta,c) ELSE GOTO 8; + + (* Do readlabel and the work below only if this is not + labeled to be a reference. + Next, we check if this is a repeat occurrence of a label. + If not, enter in the list of labels and increase the count + of labels of the kind we found.*) + + IF NOT InList(sta,SerialNo) THEN AddToList(sta); + END; (* of processing a label *) + 8:read(incoming,c) + END; (* of loop which reads characters of the file *) + + writeln('First pass completed. I am starting to write the new file.'); + + Reset(incoming); + + outputfilename:=filename+filenameend; + (*If we choose to retain the tags of the labels in the renumbered + file then its name is original file name with R appended, + if the tags are stripped from the labels, S is appended.*) + TextCreator:='*TEX'; + Rewrite(outgoing,outputfilename{,50000}); + (* Use large buffers to avoid frequent disk operation *) + + current:=empty; + FOR i:=0 TO 9 DO currentcount[i]:=0; + currentcount[1]:=FirstChapter-1; + currentcount[2]:=FirstSection-1; + + (* We need to redo current to be able to restore the implicit + parts of the labels as we encounter them on the second pass. *) + +3:read(incoming,c); + WHILE eof(incoming)=FALSE DO + BEGIN + IF ord(c) >=128 THEN BEGIN (* A label may begin here. *) + (* Check for and convert MS-DOS label tags *) + IF ord(c)=220 THEN BEGIN LabelKind:=0; c:=labeltag(0) END; + IF ((227 <= ord(c)) AND (ord(c)<= 229)) THEN BEGIN + LabelKind:= ord(c)-226; c:=labeltag(labelkind) END; + IF ((221 <= ord(c)) AND (ord(c)<= 226)) THEN BEGIN + LabelKind:= ord(c)-217; c:=labeltag(labelkind) END; {Of DOS conv.} + ForwardRef:=FALSE; + CASE c OF + '': LabelKind:=0; + '': LabelKind:=1; + '': LabelKind:=2; + '': LabelKind:=3; + '': LabelKind:=4; + '': LabelKind:=5; + '': LabelKind:=6; + '': LabelKind:=7; + '': LabelKind:=8; + '': LabelKind:=9 + OTHERWISE BEGIN (*The symbol is not a labelkind indicator*) + write(outgoing,c); GOTO 3; + END; + END; (* of CASEs. We did not GOTO 3, c is a labeltag. *) + IF LabeltagsInOutput THEN write(outgoing,c); + read(incoming,c); + IF c=chr(222) THEN c:=''; + IF c='' THEN BEGIN + ForwardRef:=TRUE; + IF LabeltagsInOutput THEN write(outgoing,c); + read(incoming,c) + END; + readlabel(sta,c); + IF NOT(InList(sta,SerialNo)) THEN BEGIN + writeln('LABEL NOT FOUND IN LIST ON SECOND PASS. ERROR'); + writeln('YOU MAY HAVE REFERRED TO A NONEXISTENT LABEL'); + writeln('OR YOU MAY HAVE ENTERED THE WRONG LEVEL NUMBER.'); + write('LabelKind: ',Labelkind,' Label in full form: '); + FOR i:=1+ImplAdj TO min(LabelKind-1, 3) DO + IF sta[i] <> '' THEN write(sta[i],'.'); + IF ((LabelKind = 0) OR (LabelKind > 3)) THEN writeln(sta[4]) + ELSE writeln(sta[LabelKind]); + writeln('Continue (with ??? in this reference)? y or n '); + readln(ans); + IF ans='n' THEN BEGIN close(incoming); close(outgoing); halt END; + write(outgoing,'???'); GOTO 9; + END; (* Of message and output, we had a reference to unknown label.*) + IF NOT ForwardRef THEN BEGIN + WITH mainlist[labelkind]^[SerialNo]^ DO BEGIN + IF (NOT secondpassoccurred)THEN BEGIN + IF (0<LabelKind) AND (labelkind<4) THEN BEGIN + currentcount[labelkind]:=currentcount[labelkind]+1; + (* This is not a reference but the label of a new *) + (* subdivision. Adjust current subdiv.*) + current[labelkind]:=oldlabel[labelkind]; + IF (currentcount[1]>FirstChapter) + OR (currentcount[2]>FirstSection) THEN BEGIN + FOR i:=max(labelkind+1,ImplAdj+2) TO 3 DO BEGIN + current[i]:=''; currentcount[i]:=0; + END; + END; + END; (* Of dealing with the label of a new heading. *) + secondpassoccurred:=TRUE; + END + ELSE IF LabeltagsInOutput THEN write(outgoing,''); + (* We have a reference.*) + END; (* Of WITH mainlist...*) + END; (* Of NOT ForwardRef *) + (* Now we are ready to get the new label and convert it to a + string. This can not be done once for all since the string + may be shorter in a reference to the item from within + the same subunit. Doing the job from scratch each time just + because we may need one or the other of two expressions + is admittedly inelegant but seldom are there many references + to one item. *) + MakeNewLabelstring(s,LabelKind, SerialNo); + write(outgoing,s); +9: IF PeriodFollows THEN write(outgoing,'.'); + END (* of assembling and writing the new label. *) + ELSE BEGIN (* ord(c)<128, certainly not a labelkind symbol*) + write(outgoing,c); + read(incoming,c); + END; (* of IF ord(c)>=128..ELSE statement. *) + END; (* Return to the beginning of the WHILE eof(incoming)=FALSE cycle *) + write(outgoing,c); (* write the last symbol of the file *) + close(outgoing);close(incoming); + writeln(chr(7),'"',outputfilename,'"', 'written on disk.'); + FOR i:=0 TO 9 DO dispose(mainlist[i]); + write('Relabel another file? (y or n)'); + readln(c); IF c='y' THEN GOTO 1; + END. |