diff options
Diffstat (limited to 'support/relabel/relabeli.pas')
-rw-r--r-- | support/relabel/relabeli.pas | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/support/relabel/relabeli.pas b/support/relabel/relabeli.pas new file mode 100644 index 0000000000..7610da260f --- /dev/null +++ b/support/relabel/relabeli.pas @@ -0,0 +1,502 @@ +PROGRAM relabeli; {Relabel program for IBM-compatible computers, to +be compiled by Turbo Pascal. + Written by Peter Ungar, 914 723 7187, June 7th, 94, Mar. 5th, 95, +Mar. 31, 95} +{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: Message asking for starting chapter number +and section number changed to disallow -1. RELABEL is programmed +to regard the character - as not part of a label. + Aslo, the message in case of a reference to a nonexistent label +was made clearer. } +{$N-} +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 loosely follow this usage, + and use "identifier" for an occurrence of a string which + could be a label or a reference. 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 identifier at its + first occurrence which is not marked as a reference + 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 identifier changed to its new + value. + + Stringarrays will hold identifiers, including those parts of the + identifiers which are implicit, i.e. if we omit the identifier(s) + of the current text unit, RELABEL will put them in. + The entries of the stringarray are: + a) If SectionReset=TRUE (3-level mode): chapteri.d., section i.d., + subsection i.d. and the identifier of the object. + 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 i.d.s and + the first entry of the string array is empty except in the + i.d. of a chapter. *) + + (* We use the following pointer construction to get around the fact + that Turbo Pascal allots a space of only 64K 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; + s,filename,outputfilename:string; + labelstring:string[51]; + sta,empty,current:stringarray; + + (* totalcount will say how many labels of the 9 kinds we have + so far. currentcount gives the counts of the 9 kinds since they + were last reset. + current contains the chapter, section + and subsection identifiers of the current text unit. *) + + 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 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 + then a label of anything except a chapter is complete without the + chapter label, and star[1]:='' for all such items. + The label of a nonreset item should not contain chapter, section + or subsection identifiers. *) + 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('section numbers then nonchapter labels should not contain'); + writeln('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= '); write(LabelKind,' '); + FOR i:=1+ImplAdj TO k+ImplAdj-1 DO write(sta[i],'.'); + writeln(sta[k+ImplAdj]); + write(' 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 item 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:= chr(220); (* at-large item *) + 1: LabelTag:= chr(227); (* chapter *) + 2: LabelTag:= chr(228); (* section *) + 3: LabelTag:= chr(229); (* subsection *) + 4: LabelTag:= chr(221); (* figure *) + 5: LabelTag:= chr(222); (* formula *) + 6: LabelTag:= chr(223); (* problem *) + 7: LabelTag:= chr(224); (* definition *) + 8: LabelTag:= chr(225); (* theorem *) + 9: LabelTag:= chr(226); (* lemma *) + 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 directory as'); + writeln('RELABEL, then give path name.'); + readln(filename); + writeln(' Name of relabeled file: '); + readln(OutputFileName); + writeln('Is the labeling based on 3 levels (Chapter, Section, Subsection'); + writeln('or 2 levels (Section, Subsection)? (Input 3 or 2)'); + 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 in'); + writeln('the new file if they are all the current ones (y or n)'); + writeln('(Chapter numbers are included only in 3-level mode.)'); + readln(ans); omit:=ans='n'; + writeln('Do you want the label tags to remain in the output?'); + readln(ans); LabeltagsInOutput:=ans='y'; + FOR i:=0 TO 9 DO currentcount[i]:=0; + TotalCount:=CurrentCount; + writeln('Input the starting number >= 0 you want for chapters'); + 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]); + + Assign(incoming,filename); + Reset(incoming); + read(incoming, c); + WHILE eof(incoming)=FALSE DO + BEGIN + IF ord(c) > 128 THEN BEGIN + CASE c OF (* cases of Macintosh labels *) + '': LabelKind:=0; + '': LabelKind:=1; + '': LabelKind:=2; + '': LabelKind:=3; + '': LabelKind:=4; + '': LabelKind:=5; + '': LabelKind:=6; + '': LabelKind:=7; + '': LabelKind:=8; + '': LabelKind:=9; + ELSE (* 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; (* c is not a label tag *) + END; + read(incoming, c); + IF ((ord(c) = 196) OR (ord(c)=222)) THEN GOTO 8 + ELSE readlabel(sta,c); + + (* 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 WHILE loop which reads characters of the file *) + + writeln('First pass completed. I am starting to write the new file.'); + + Reset(incoming); + + Assign(outgoing, outputfilename); + Rewrite(outgoing); + + 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. *) + CASE c OF +(* If we have a labelkind indicator used with Macintosh, convert it. *) + '': c:=chr(220); + '': c:=chr(227); + '': c:=chr(228); + '': c:=chr(229); + '': c:=chr(225); + '': c:=chr(226); + '': c:=chr(224); + '': c:=chr(222); + '': c:=chr(221); + '': c:=chr(223); + END; (* Of replacing Macintosh labels. *) + ForwardRef:=FALSE; + LabelKind:=ord(c)-220; + IF ((labelkind < 0) OR (LabelKind > 9)) THEN BEGIN + (*The symbol is not a labelkind indicator. Write & exit.*) + write(outgoing,c); GOTO 3 + END + ELSE IF LabelKind > 6 THEN LabelKind:=LabelKind-6 + ELSE IF LabelKind>0 THEN LabelKind:= LabelKind+3; + (* End of determining LabelKind *) + IF LabeltagsInOutput THEN write(outgoing,c); + read(incoming,c); + IF c=chr(196) THEN c:=chr(222); {Translate Macintosh forward ref. ind.} + IF c=chr(222) 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.'); + writeln('You may have referred to a nonexistent label'); + writeln('or the level number you entered may be wrong.'); + write('LabelKind: ', LabelKind,' Label: '); + 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]); + write('Should I go on, 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 +(* Should be here? currentcount[labelkind]:=currentcount[labelkind]+1; *) + 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. |