elwell@osu-eddie.UUCP (Clayton M. Elwell) (09/05/86)
This is part 2 of the DVIDOC posting. Make sure and unshar part 1 first. --------------------------------CUT HERE---------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to complete the file: # dvidoc.web # This archive created: Fri Sep 5 12:27:57 1986 export PATH; PATH=/bin:$PATH if test ! -f 'dvidoc.web' then echo shar: please extract part 1 first... exit 1 else cat << \SHAR_EOF >> 'dvidoc.web' @.DVIDOC capacity exceeded...@> font_name[nf+1]:=font_name[nf]+n+p; write(term_out,'Font ',e:0,': '); if n+p=0 then write(term_out,'null font name!') @.null font name@> else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte; incr(nf); print_font(nf-1); decr(nf) @ @<Load the new font, unless there are problems@>= begin @<Move font name into the |cur_name| string@>; open_tfm_file; if eof(tfm_file) then write(term_out,'---not loaded, TFM file can''t be opened!') @.TFM file can\'t be opened@> else begin if (q<=0)or(q>=@'1000000000) then write(term_out,'---not loaded, bad scale (',q:0,')!') @.bad scale@> else if (d<=0)or(d>=@'1000000000) then write(term_out,'---not loaded, bad design size (',d:0,')!') @.bad design size@> else if in_TFM(q) then @<Finish loading the new font info@>; end; write_ln(term_out,' '); end @ @<Finish loading...@>= begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''} if (c<>0)and(tfm_check_sum<>0)and(c<>tfm_check_sum) then begin write_ln(term_out,'---beware: check sums do not agree!'); @.beware: check sums do not agree@> @.check sums do not agree@> write_ln(term_out,' (',c:0,' vs. ',tfm_check_sum:0,')'); write(term_out,' '); end; write(term_out,'---loaded at size ',q:0,' DVI units'); d:=trunc((100.0*horiz_conv*q)/(true_horiz_conv*d)+0.5); if d<>100 then begin write_ln(term_out,' '); write(term_out,' (this font is magnified ',d:0,'%)'); end; @.this font is magnified@> incr(nf); {now the new font is officially present} end @ If |p=0|, i.e., if no font directory has been specified, \.{DVIDOC} is supposed to use the default font directory, which is a system-dependent place where the standard fonts are kept. The string variable |default_directory| contains the name of this area. @^system dependencies@> @^changed module@> @d default_directory_name=='TEXFONTS:' {changed to the correct name} @d default_directory_name_length=9 {changed to the correct length} @<Glob...@>= @!default_directory:packed array[1..default_directory_name_length] of char; @ @<Set init...@>= default_directory:=default_directory_name; @ The string |cur_name| is supposed to be set to the external name of the \.{TFM} file for the current font. This usually means that we need to prepend the name of the default directory, and to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters to upper case, since |cur_name| is a \PASCAL\ string. @^system dependencies@> @<Move font name into the |cur_name| string@>= for k:=1 to name_length do cur_name[k]:=' '; if p=0 then begin for k:=1 to default_directory_name_length do cur_name[k]:=default_directory[k]; r:=default_directory_name_length; end else r:=0; for k:=font_name[nf] to font_name[nf+1]-1 do begin incr(r); if r+4>name_length then abort('DVIDOC capacity exceeded (max font name length=', name_length:0,')!'); @.DVIDOC capacity exceeded...@> if (names[k]>="a")and(names[k]<="z") then cur_name[r]:=xchr[names[k]-@'40] else cur_name[r]:=xchr[names[k]]; end; cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M' @* Low level output routines. Characters set by the \.{DVI} file are placed in |page_buffer|, a two dimensional array of characters with one element for each print position on the page. The |page_buffer| is cleared at the beginning of each page and printed at the end of each page. |doc_file|, the file to which the document is destined, is an ordinary text file. To optimize the initialization and printing of |page_buffer|, a high water mark line number, |page_hwm|, is kept to indicate the last line that contains any printable characters, and for each line a high water mark character number, |line_hwm|, is kept to indicate the location of the last printable character in the line. @<Glob...@>= @!doc_file:text_file; @!page_buffer:packed array[1..page_width_max,1..page_length_max] of ascii_code; {storage for a document page} @!line_hwm:array[1..page_length_max] of 0..page_width_max; {high water marks for each line} @!page_hwm: 0..page_length_max; {high water mark for page} @ |doc_file| needs to be opened. @<Set initial values@>= rewrite(doc_file); @ The |flush_page| procedure will print the |page_buffer|. @p procedure flush_page; var i:0..page_width_max; j:0..page_length_max; begin for j := 1 to page_hwm do begin for i := 1 to line_hwm[j] do write (doc_file, xchr[page_buffer[i,j]]); write_ln (doc_file) end; write (doc_file, chr(12)) {end the page with a form feed} end; @ The |empty_page| procedure will empty the |page_buffer| data structure. @p procedure empty_page; begin page_hwm := 0 end; @ And the |out_char| procedure puts something into it. The usual printable ascii characters will be put into the buffer as is. Non-printable characters, including the blank, will be put into the buffer as question mark chracters. @p procedure out_char(p,hh,vv:integer); var i:1..page_width_max; j:1..page_length_max; {|hh| and |vv| range from zero up while |i| and |j| range from one up.} k: integer; c: ascii_code; begin if (p>" ")and(p<="~") then c:=p else c:=xord['?']; if (hh>page_width_max-1) or (vv>page_length_max-1) then begin write_ln (term_out); write (term_out, 'Character "', xchr[c], '" set at column ', hh+1:0); write_ln (term_out, ' and row ', vv+1:0, ','); write (term_out, 'outside the range of DVIDOC ('); @.outside the range of DVIDOC@> write (term_out, page_width_max:0, ',', page_length_max:0, ').'); write_ln (term_out) end else begin i := hh + 1; j := vv + 1; if j>page_hwm then begin {initialize any as yet untouched lines} for k := page_hwm+1 to j do line_hwm[k]:=0; page_hwm := j end; if i>line_hwm[j] then begin {initialize any as yet untouched characters} for k := line_hwm[j]+1 to i do page_buffer[k,j] := xord[' ']; line_hwm[j] := i end; page_buffer[i,j] := c {put the character in its place} end end; @* Translation to symbolic form. The main work of \.{DVIDOC} is accomplished by the |do_page| procedure, which produces the output for an entire page, assuming that the |bop| command for that page has already been processed. This procedure is essentially an interpretive routine that reads and acts on the \.{DVI} commands. @ The definition of \.{DVI} files refers to six registers, $(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units. In practice, we also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$, since it is not always true that |hh=horiz_pixel_round(h)| or |vv=vert_pixel_round(v)|. The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays called |hstack|, $\ldotss$, |zstack|, |hhstack|, and |vvstack|. @<Glob...@>= @!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values} @!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack: array [0..stack_size] of integer; {pushed down values in \.{DVI} units} @!hhstack,@!vvstack: array [0..stack_size] of integer; {pushed down values in pixels} @ Three characteristics of the pages (their |max_v|, |max_h|, and |max_s|) are specified in the postamble, and a warning message is printed if these limits are exceeded. Actually |max_v| is set to the maximum height plus depth of a page, and |max_h| to the maximum width, for purposes of page layout. Since characters can legally be set outside of the page boundaries, it is not an error when |max_v| or |max_h| is exceeded. But |max_s| should not be exceeded. The postamble also specifies the total number of pages; \.{DVIDOC} checks to see if this total is accurate. @<Glob...@>= @!max_v:integer; {the value of |abs(v)| should probably not exceed this} @!max_h:integer; {the value of |abs(h)| should probably not exceed this} @!max_s:integer; {the stack depth should not exceed this} @!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels} @!total_pages:integer; {the stated total number of pages} @!page_count:integer; {the total number of pages seen so far} @ @<Set init...@>= max_v:=@'17777777777; max_h:=@'17777777777; max_s:=stack_size+1;@/ max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0; @ Before we get into the details of |do_page|, it is convenient to consider a simpler routine that computes the first parameter of each opcode. @d four_cases(#)==#,#+1,#+2,#+3 @d eight_cases(#)==four_cases(#),four_cases(#+4) @d sixteen_cases(#)==eight_cases(#),eight_cases(#+8) @d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16) @d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32) @p function first_par(o:eight_bits):integer; begin case o of sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64): first_par:=o-set_char_0; set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte; set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes; set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes; right1,w1,x1,down1,y1,z1: first_par:=signed_byte; right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair; right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio; set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3, fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad; nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0; w0: first_par:=w; x0: first_par:=x; y0: first_par:=y; z0: first_par:=z; sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0; end; end; @ Here are two other subroutines that we need: They compute the number of pixels in the height or width of a rule. Characters and rules will line up properly if the sizes are computed precisely as specified here. (Since |horiz_conv| and |vert_conv| are computed with some floating-point roundoff error, in a machine-dependent way, format designers who are tailoring something for a particular resolution should not plan their measurements to come out to an exact integer number of pixels; they should compute things so that the rule dimensions are a little less than an integer number of pixels, e.g., 4.99 instead of 5.00.) @p function horiz_rule_pixels(x:integer):integer; {computes $\lceil|horiz_conv|\cdot x\rceil$} var n:integer; begin n:=trunc(horiz_conv*x); if n<horiz_conv*x then horiz_rule_pixels:=n+1 @+ else horiz_rule_pixels:=n; end; function vert_rule_pixels(x:integer):integer; {computes $\lceil|vert_conv|\cdot x\rceil$} var n:integer; begin n:=trunc(vert_conv*x); if n<vert_conv*x then vert_rule_pixels:=n+1 @+ else vert_rule_pixels:=n; end; @ Strictly speaking, the |do_page| procedure is really a function with side effects, not a `\&{procedure}'; it returns the value |false| if \.{DVIDOC} should be aborted because of some unusual happening. The subroutine is organized as a typical interpreter, with a multiway branch on the command code followed by |goto| statements leading to routines that finish up the activities common to different commands. We will use the following labels: @d fin_set=41 {label for commands that set or put a character} @d fin_rule=42 {label for commands that set or put a rule} @d move_right=43 {label for commands that change |h|} @d move_down=44 {label for commands that change |v|} @d show_state=45 {label for commands that change |s|} @d change_font=46 {label for commands that change |cur_font|} @ Some \PASCAL\ compilers severely restrict the length of procedure bodies, so we shall split |do_page| into two parts, one of which is called |special_cases|. The different parts communicate with each other via the global variables mentioned above, together with the following ones: @<Glob...@>= @!s:integer; {current stack size} @!ss:integer; {stack size to print} @!cur_font:integer; {current internal font number} @ Here is the overall setup. @p @<Declare the function called |special_cases|@>@; function do_page:boolean; label fin_set,fin_rule,move_right,show_state,done,9998,9999; var o:eight_bits; {operation code of the current command} @!p,@!q:integer; {parameters of the current command} @!a:integer; {byte number of the current command} i,j:integer; {for loop indices for setting rules} begin empty_page; cur_font:=nf; {set current font undefined} s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; hh:=0; vv:=0; {initialize the state variables} while true do @<Translate the next command in the \.{DVI} file; |goto 9999| with |do_page=true| if it was |eop|; |goto 9998| if premature termination is needed@>; 9998: write_ln(term_out,'!'); do_page:=false; 9999: end; @ @d error(#)==write(term_out,' ',#) @<Translate the next command...@>= begin a:=cur_loc; o:=get_byte; p:=first_par(o); if eof(dvi_file) then abort('the file ended prematurely!'); @.the file ended prematurely@> @<Start translation of command |o| and |goto| the appropriate label to finish the job@>; fin_set: @<Finish a command that either sets or puts a character, then |goto move_right| or |done|@>; fin_rule: @<Finish a command that either sets or puts a rule, then |goto move_right| or |done|@>; move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>; show_state: ; done: ; end @ The multiway switch in |first_par|, above, was organized by the length of each command; the one in |do_page| is organized by the semantics. @<Start translation...@>= if o<set_char_0+128 then @<Translate a |set_char| command@> else case o of four_cases(set1): begin out_char(p,hh,vv); goto fin_set; end; set_rule: begin goto fin_rule; end; put_rule: begin goto fin_rule; end; @t\4@>@<Cases for commands |nop|, |bop|, $\ldotss$, |pop|@>@; @t\4@>@<Cases for horizontal motion@>@; othercases if special_cases(o,p,a) then goto done@+else goto 9998 endcases @ @<Declare the function called |special_cases|@>= function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean; label change_font,move_down,done,9998; var q:integer; {parameter of the current command} @!k:integer; {loop index} @!bad_char:boolean; {has a non-ascii character code appeared in this \\{xxx}?} @!pure:boolean; {is the command error-free?} begin pure:=true; case o of four_cases(put1): begin goto done; end; @t\4@>@<Cases for vertical motion@>@; @t\4@>@<Cases for fonts@>@; four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>; pre: begin error('preamble command within a page!'); goto 9998; end; @.preamble command within a page@> post,post_post: begin error('postamble command within a page!'); goto 9998; @.postamble command within a page@> end; othercases begin error('undefined command ',o:0,'!'); goto done; @.undefined command@> end endcases; move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>; change_font: @<Finish a command that changes the current font, then |goto done|@>; 9998: pure:=false; done: special_cases:=pure; end; @ @<Cases for commands |nop|, |bop|, $\ldotss$, |pop|@>= nop: begin goto done; end; bop: begin error('bop occurred before eop'); goto 9998; @.bop occurred before eop@> end; eop: begin if s<>0 then error('stack not empty at end of page (level ', s:0,')!'); @.stack not empty...@> do_page:=true; flush_page; goto 9999; end; push: begin if s=max_s_so_far then begin max_s_so_far:=s+1; if s=max_s then error('deeper than claimed in postamble!'); @.deeper than claimed...@> @.push deeper than claimed...@> if s=stack_size then begin error('DVIDOC capacity exceeded (stack size=', stack_size:0,')'); goto 9998; end; end; hstack[s]:=h; vstack[s]:=v; wstack[s]:=w; xstack[s]:=x; ystack[s]:=y; zstack[s]:=z; hhstack[s]:=hh; vvstack[s]:=vv; incr(s); ss:=s-1; goto show_state; end; pop: begin if s=0 then error('Pop illegal at level zero!') else begin decr(s); hh:=hhstack[s]; vv:=vvstack[s]; h:=hstack[s]; v:=vstack[s]; w:=wstack[s]; x:=xstack[s]; y:=ystack[s]; z:=zstack[s]; end; ss:=s; goto show_state; end; @ Rounding to the nearest pixel is best done in the manner shown here, so as to be inoffensive to the eye: When the horizontal motion is small, like a kern, |hh| changes by rounding the kern; but when the motion is large, |hh| changes by rounding the true position |h| so that accumulated rounding errors disappear. @d out_space==if abs(p)>=font_space[cur_font] then begin hh:=horiz_pixel_round(h+p); end else hh:=hh+horiz_pixel_round(p); q:=p; goto move_right @<Cases for horizontal motion@>= four_cases(right1):begin out_space; end; w0,four_cases(w1):begin w:=p; out_space; end; x0,four_cases(x1):begin x:=p; out_space; end; @ Vertical motion is done similarly, but with the threshold between ``small'' and ``large'' increased by a factor of five. The idea is to make fractions like ``$1\over2$'' round consistently, but to absorb accumulated rounding errors in the baseline-skip moves. @d out_vmove==if abs(p)>=5*font_space[cur_font] then vv:=vert_pixel_round(v+p) else vv:=vv+vert_pixel_round(p); goto move_down @<Cases for vertical motion@>= four_cases(down1):begin out_vmove; end; y0,four_cases(y1):begin y:=p; out_vmove; end; z0,four_cases(z1):begin z:=p; out_vmove; end; @ @<Cases for fonts@>= sixty_four_cases(fnt_num_0): begin goto change_font; end; four_cases(fnt1): begin goto change_font; end; four_cases(fnt_def1): begin define_font(p); goto done; end; @ @<Translate an |xxx| command and |goto done|@>= begin write(term_out,'xxx'''); bad_char:=false; for k:=1 to p do begin q:=get_byte; if (q>="!")and(q<="~") then write(term_out,xchr[q]) else bad_char:=true end; write(term_out,''''); if bad_char then error('non-ascii character in xxx command!'); @.non-ascii character...@> goto done; end @ @<Translate a |set_char|...@>= begin out_char(p,hh,vv) end @ @<Finish a command that either sets or puts a character...@>= if font_ec[cur_font]=256 then p:=256; {width computation for oriental fonts} if (p<font_bc[cur_font])or(p>font_ec[cur_font]) then q:=invalid_width else q:=char_width(cur_font)(p); if q=invalid_width then begin error('character ',p:0,' invalid in font '); @.character $c$ invalid...@> print_font(cur_font); if cur_font<>nf then write(term_out,'!'); end; if o>=put1 then goto done; if q=invalid_width then q:=0 else hh:=hh+char_pixel_width(cur_font)(p); goto move_right @ @<Finish a command that either sets or puts a rule...@>= q:=signed_quad; if (p>0) and (q>0) then for i:=hh to hh+horiz_rule_pixels(q)-1 do for j:=vv downto vv-vert_rule_pixels(p)+1 do out_char(xord['-'],i,j); if o=put_rule then goto done; hh:=hh+horiz_rule_pixels(q); goto move_right @ Since \.{DVIDOC} is intended to diagnose strange errors, it checks carefully to make sure that |h| and |v| do not get out of range. Normal \.{DVI}-reading programs need not do this. @d infinity==@'17777777777 {$\infty$ (approximately)} @<Finish a command that sets |h:=h+q|, then |goto done|@>= if (h>0)and(q>0) then if h>infinity-q then begin error('arithmetic overflow! parameter changed from ', @.arithmetic overflow...@> q:0,' to ',infinity-h:0); q:=infinity-h; end; if (h<0)and(q<0) then if -h>q+infinity then begin error('arithmetic overflow! parameter changed from ', q:0, ' to ',(-h)-infinity:0); q:=(-h)-infinity; end; h:=h+q; if abs(h)>max_h_so_far then begin max_h_so_far:=abs(h); if abs(h)>max_h then error('warning: |h|>',max_h_so_far:0,'!'); @.warning: |h|...@> end; goto done @ @<Finish a command that sets |v:=v+p|, then |goto done|@>= if (v>0)and(p>0) then if v>infinity-p then begin error('arithmetic overflow! parameter changed from ', @.arithmetic overflow...@> p:0,' to ',infinity-v:0); p:=infinity-v; end; if (v<0)and(p<0) then if -v>p+infinity then begin error('arithmetic overflow! parameter changed from ', p:0, ' to ',(-v)-infinity:0); p:=(-v)-infinity; end; v:=v+p; if abs(v)>max_v_so_far then begin max_v_so_far:=abs(v); if abs(v)>max_v then error('warning: |v|>',max_v_so_far:0,'!'); @.warning: |v|...@> end; goto done @ @<Finish a command that changes the current font...@>= font_num[nf]:=p; cur_font:=0; while font_num[cur_font]<>p do incr(cur_font); goto done @* Skipping pages. @ Global variables called |old_backpointer| and |new_backpointer| are used to check whether the back pointers are properly set up. Another one tells whether we have already found the starting page. @<Glob...@>= @!old_backpointer:integer; {the previous |bop| command location} @!new_backpointer:integer; {the current |bop| command location} @!started:boolean; {has the starting page been found?} @ @<Set init...@>= old_backpointer:=-1; started:=false; @ @<Pass a |bop| command, setting up the |count| array@>= new_backpointer:=cur_loc-1; incr(page_count); for k:=0 to 9 do count[k]:=signed_quad; if signed_quad<>old_backpointer then write_ln(term_out,'backpointer in byte ',cur_loc-4:0, ' should be ',old_backpointer:0,'!'); @.backpointer...should be p@> old_backpointer:=new_backpointer @* Using the backpointers. First comes a routine that illustrates how to find the postamble quickly. @<Find the postamble, working back from the end@>= n:=dvi_length; if n<57 then bad_dvi('only ',n:0,' bytes long'); @.only n bytes long@> m:=n-4; repeat if m=0 then bad_dvi('all 223s'); @.all 223s@> move_to_byte(m); k:=get_byte; decr(m); until k<>223; if k<>id_byte then bad_dvi('ID byte is ',k:0); @.ID byte is wrong@> move_to_byte(m-3); q:=signed_quad; if (q<0)or(q>m-36) then bad_dvi('post pointer ',q:0,' at byte ',m-3:0); @.post pointer is wrong@> move_to_byte(q); k:=get_byte; if k<>post then bad_dvi('byte ',q:0,' is not post'); @.byte n is not post@> post_loc:=q; first_backpointer:=signed_quad @ Note that the last steps of the above code save the locations of the the |post| byte and the final |bop|. We had better declare these global variables, together with another one that we will need shortly. @<Glob...@>= @!post_loc:integer; {byte location where the postamble begins} @!first_backpointer:integer; {the pointer following |post|} @!start_loc:integer; {byte location of the first page to process} @ The next little routine shows how the backpointers can be followed to move through a \.{DVI} file in reverse order. Ordinarily a \.{DVI}-reading program would do this only if it wants to print the pages backwards or if it wants to find a specified starting page that is not necessarily the first page in the file; otherwise it would of course be simpler and faster just to read the whole file from the beginning. @<Count the pages and move to the starting page@>= q:=post_loc; p:=first_backpointer; start_loc:=-1; if p>=0 then repeat {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer} if p>q-46 then bad_dvi('page link ',p:0,' after byte ',q:0); @.page link wrong...@> q:=p; move_to_byte(q); k:=get_byte; if k=bop then incr(page_count) else bad_dvi('byte ',q:0,' is not bop'); @.byte n is not bop@> for k:=0 to 9 do count[k]:=signed_quad; if start_match then start_loc:=q; p:=signed_quad; until p<0; if start_loc<0 then abort('starting page number could not be found!'); move_to_byte(start_loc+1); old_backpointer:=start_loc; for k:=0 to 9 do count[k]:=signed_quad; p:=signed_quad; started:=true @* Reading the postamble. Now imagine that we are reading the \.{DVI} file and positioned just four bytes after the |post| command. That, in fact, is the situation, when the following part of \.{DVIDOC} is called upon to read, translate, and check the rest of the postamble. @p procedure read_postamble; var k:integer; {loop index} @!p,@!q,@!m:integer; {general purpose registers} begin move_to_byte(cur_loc+12); {skip over numerator, denominator, and magnification} max_v:=signed_quad; max_h:=signed_quad;@/ max_s:=get_two_bytes; total_pages:=get_two_bytes;@/ @<Process the font definitions of the postamble@>; @<Make sure that the end of the file is well-formed@>; end; @ When we get to the present code, the |post_post| command has just been read. @<Make sure that the end of the file is well-formed@>= q:=signed_quad; m:=get_byte; k:=cur_loc; m:=223; while (m=223)and not eof(dvi_file) do m:=get_byte; if not eof(dvi_file) then abort('signature in byte ',cur_loc-1:0, @.signature...should be...@> ' should be 223!') else if cur_loc<k+4 then write_ln(term_out,'not enough signature bytes at end of file (', @.not enough signature bytes...@> cur_loc-k:0,')'); @ @<Process the font definitions...@>= repeat k:=get_byte; if (k>=fnt_def1)and(k<fnt_def1+4) then begin p:=first_par(k); define_font(p); write_ln(term_out,' '); k:=nop; end; until k<>nop @* The main program. Now we are ready to put it all together. This is where \.{DVIDOC} starts, and where it ends. @p begin initialize; {get all variables initialized} dialog; {set up all the options} @<Process the preamble@>; @<Find the postamble, working back from the end@>; in_postamble:=true; read_postamble; in_postamble:=false; @<Count the pages and move to the starting page@>; if not in_postamble then @<Translate up to |max_pages| pages@>; final_end:end. @ The main program needs a few global variables in order to do its work. @<Glob...@>= @!k,@!m,@!n,@!p,@!q:integer; {general purpose registers} @ A \.{DVI}-reading program that reads the postamble first need not look at the preamble; but \.{DVIDOC} looks at the preamble in order to do error checking, and to display the introductory comment. @<Process the preamble@>= open_dvi_file; p:=get_byte; {fetch the first byte} if p<>pre then bad_dvi('First byte isn''t start of preamble!'); @.First byte isn't...@> p:=get_byte; {fetch the identification byte} @<Compute the conversion factor@>; p:=get_byte; {fetch the length of the introductory comment} write(term_out,''''); while p>0 do begin decr(p); write(term_out,xchr[get_byte]); end; write_ln(term_out,'''') @ The conversion factors |horiz_conv| and |vert_conv| are figured as follows: There are exactly |n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch, and |horiz_resolution| or |vert_resolution| characters per inch. Then we have to adjust this by the stated amount of magnification. @<Compute the conversion factor@>= numerator:=signed_quad; denominator:=signed_quad; if numerator<=0 then bad_dvi('numerator is ',numerator:0); @.numerator is wrong@> if denominator<=0 then bad_dvi('denominator is ',denominator:0); @.denominator is wrong@> horiz_conv:=(numerator/254000.0)*(horiz_resolution/denominator); vert_conv:=(numerator/254000.0)*(vert_resolution/denominator); mag:=signed_quad; if new_mag>0 then mag:=new_mag else if mag<=0 then bad_dvi('magnification is ',mag:0); @.magnification is wrong@> true_horiz_conv:=horiz_conv; horiz_conv:=true_horiz_conv*(mag/1000.0); true_vert_conv:=vert_conv; vert_conv:=true_vert_conv*(mag/1000.0); @ The code shown here uses a convention that has proved to be useful: If the starting page was specified as, e.g., `\.{1.*.-5}', then all page numbers in the file are displayed by showing the values of counts 0, 1, and@@2, separated by dots. Such numbers can, for example, be displayed on the console of a printer when it is working on that page. @<Translate up to...@>= begin while max_pages>0 do begin decr(max_pages); write_ln(term_out); write(term_out,'Beginning of page '); for k:=0 to start_vals do begin write(term_out,count[k]:0); if k<start_vals then write(term_out,'.') else write_ln(term_out); end; if not do_page then abort('page ended unexpectedly!'); @.page ended unexpectedly@> repeat k:=get_byte; if (k>=fnt_def1)and(k<fnt_def1+4) then begin p:=first_par(k); define_font(p); k:=nop; end; until k<>nop; if k=post then begin in_postamble:=true; goto done; end; if k<>bop then bad_dvi('byte ',cur_loc-1:0,' is not bop'); @.byte n is not bop@> @<Pass a |bop|...@>; end; done:end @* System-dependent changes. This module should be replaced, if necessary, by changes to the program that are necessary to make \.{DVIDOC} work at a particular installation. It is usually best to design your change file so that all changes to previous modules preserve the module numbering; then everybody's version will be consistent with the printed program. More extensive changes, which introduce new modules, can be inserted here; then only the index itself will get a new module number. @^system dependencies@> @* Index. Pointers to error messages appear here together with the section numbers where each ident\-i\-fier is used. SHAR_EOF fi # end of overwriting check # End of shell archive exit 0 -- ---------------------------------------------------------------------- Computers will never replace the Clayton Elwell wastebasket when it comes to Elwell@Ohio-State.ARPA streamlining office work. ...!cbosgd!osu-eddie!elwell ----------------------------------------------------------------------