summaryrefslogtreecommitdiff
path: root/support/relabel/relabeli.pas
diff options
context:
space:
mode:
Diffstat (limited to 'support/relabel/relabeli.pas')
-rw-r--r--support/relabel/relabeli.pas502
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.