@x \def\PASCAL{Pascal} @y \def\PASCAL{Pascal} \def\pv{VAX/VMS \PASCAL} @z @x \def\({} % kludge for alphabetizing certain module names @y \def\({} % kludge for alphabetizing certain module names % provide a more normal appearance for funny VAX/VMS names \def\=#1{\hbox{{\it \def\${{\sl\char`\$}}#1\/\kern.05em}}} % italic type for verbatim string \let\9=\= @z @x \centerline{(Version 1.0)} \vfill} \pageno=\contentspagenumber \advance\pageno by 1 @y \centerline{(Version 1.0)} \centerline{(VAX/VMS Version 1.0.0, April 1986)} \vfill \centerline{\hsize 5in\baselineskip9pt \vbox{\ninerm\noindent The changes required to create this VAX/VMS version of \.{SCANWEAVE} are the sections marked with asterisks (*) below.}}} \pageno=1 @z @x @d banner=='This is SCANWEAVE, Version 1.0' @y For the VAX/VMS versions of \.{SCANWEAVE}, we add a second decimal point and number to the main version number, so we can differentiate between changes to the standard \.{SCANWEAVE}\ and changes local to the VAX/VMS version. @d banner=='This is SCANWEAVE, VAX/VMS Version 1.0.0' @z @x program SCANWEAVE(@!tex_file,@!out_tex_file); @y program SCANWEAVE; @z @x @@/ @y @@/ @@/ @z @x @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @y @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @z @x @d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @y @d stat== {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats== {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @z @x @ The \PASCAL\ compiler used to develop this system has ``compiler directives'' that can appear in comments whose first character is a dollar sign. In production versions of \.{SCANWEAVE} these directives tell the compiler that @^system dependencies@> it is safe to avoid range checks and to leave out the extra code it inserts for the \PASCAL\ debugger's benefit, although interrupts will occur if there is arithmetic overflow. @= @{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead} @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging} @y @ \pv\ allows the user to specify ``attributes'' in the program source that affect the translation of the program into machine language. Attributes are denoted by keywords; they are set off from the rest of the source text by enclosing them in brackets. The directives shown below cause range checking and other redundant code to be eliminated when \.{SCANWEAVE} is being generated, and they give the program access to definitions in the ``environment'' file \.{sys\$system:starlet.pen}, which describes every VAX/VMS system service and VAX-11 RMS routine. Arithmetic overflow will be detected in all cases. @^system dependencies@> @^Overflow in arithmetic@> @= [check(none),inherit('sys$library:starlet')] {no range check, catch arithmetic overflow, no debug overhead} @z @x @d othercases == others: {default for cases not listed explicitly} @y @d othercases == otherwise {default for cases not listed explicitly} @z @x @d last_text_char=127 {ordinal number of the largest element of |text_char|} @= @!text_file=packed file of text_char; @y @d last_text_char=127 {ordinal number of the largest element of |text_char|} @d text_file==text @z @x @d print(#)==write(term_out,#) {`|print|' means write on the terminal} @d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line} @d new_line==write_ln(term_out) {start new line} @y @d print(#)==write_ln(term_out,#) {`|print|' means write on the terminal} @d print_ln(#)==write_ln(term_out,#,chr(13),chr(10)) {`|print|' and then start new line} @d new_line==write_ln(term_out,chr(13),chr(10)) {start new line} @z @x certain file will appear on the user's terminal. Here is one way to do this on the \PASCAL\ system that was used in \.{TANGLE}'s initial development: @^system dependencies@> @= rewrite(term_out,'TTY:'); {send |term_out| output to the terminal} @y certain file will appear on the user's terminal. Here is how to open the terminal file under \pv. @^system dependencies@> @= @=open@>(term_out,'sys$output',@=carriage_control@>:=none, @=record_length@>:=16383); rewrite(term_out); @z @x @d update_terminal == break(term_out) {empty the terminal output buffer} @y @d update_terminal == {empty the terminal output buffer} @z @x or |carriage_return|. @p function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} var final_limit:0..buf_size; {|limit| without trailing blanks} begin limit:=0; final_limit:=0; if eof(f) then input_ln:=false else begin while not eoln(f) do begin buffer[limit]:=xord[f^]; get(f); incr(limit); if buffer[limit-1]<>" " then final_limit:=limit; if limit=buf_size then begin while not eoln(f) do get(f); decr(limit); {keep |buffer[buf_size]| empty} print_nl('! Input line too long'); loc:=0; error; @.Input line too long@> end; end; read_ln(f); limit:=final_limit; input_ln:=true; end; end; @y or |carriage_return|. Since the inner loop of |input_ln| is part of \.{SCANWEAVE}'s ``inner loop''---each character of input comes in at this place---it is wise to reduce system overhead by making use of special routines that read in an entire array of characters at once. \pv\ allows us to use a `|varying|' string to read an entire line from the file. @^system dependencies@> @^inner loop@> @p function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} label done; var @!aux_buf:varying[buf_size] of char; {intermediate input buffer} k:0..buf_size; {index into |buffer|} begin limit:=0; if status(f)<>0 then input_ln:=false else begin read_ln(f,aux_buf,@=error@>:=@=continue@>); limit:=@=length@>(aux_buf); if limit>=buf_size then begin print_nl('! Input line too long'); loc:=0; error; @.Input line too long@> limit:=buf_size-1; end; for k:=1 to limit do buffer[k-1]:=xord[aux_buf[k]]; while limit>1 do if buffer[limit-1]=" " then decr(limit) @+else goto done; done: input_ln:=true; end; end; @z @x @p procedure output_ln(var f:text_file); {outputs a line } var @!ch : char; { current output character } @!temp : 0..buf_size; begin if limit > 0 then begin for temp := 0 to limit - 1 do begin ch := xchr[buffer[temp]]; f^ := ch; put(f); end; end; write_ln(f); end ; @y @p procedure output_ln(var f:text_file); {outputs a line } var @!ch : char; { current output character } @!temp : 0..buf_size; @!aux_buf:varying[buf_size] of char; {intermediate input buffer} begin if limit > 0 then begin for temp := 0 to limit - 1 do begin aux_buf[temp+1] := xchr[buffer[temp]]; end; end; aux_buf.length := limit; write_ln(f,aux_buf,@=error@>:=@=continue@>); end ; @z @x @t\4\4@>{here files should be closed if the operating system requires it} @; @y if history(out_tex_file,@=disposition@>:=@=save@>,@=error@>:=@=continue@>); end; @; @; @z @x This module should be replaced, if necessary, by changes to the program that are necessary to make \.{SCANWEAVE} 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. @y Here are the remaining changes to the program that are necessary to make \.{SCANWEAVE} work on VAX/VMS. @ Here is the stuff for magic file operations. @^system dependencies@> @= @!unsafe_file=[unsafe] file of char;@/ @!fab_pointer=^@=fab$type@>;@/ @:fab\$type@> @!nam_pointer=^@=nam$type@>; @:nam\$type@> @ @^system dependencies@> @= function@= pas$fab@>(var f:unsafe_file):fab_pointer; external;@t\2@>@/ @:pas\$fab@> @ VAX/VMS command argument names. @^system dependencies@> @d arg_size == 255 @= @!arg_buf=packed array[1..arg_size] of char; @ Temporary buffer for storing argument strings. @= @!arg:arg_buf; @!arg_len:0..arg_size; @ VAX/VMS CLI interface routines. @^system dependencies@> @= @=cli$_absent@>=@"000381F0;@/ @:cli\$_absent@> @=cli$_concat@>=@"0003FD29;@/ @:cli\$_concat@> @=cli$_defaulted@>=@"0003FD21;@/ @:cli\$_defaulted@> @=cli$_locneg@>=@"00038230;@/ @:cli\$_locneg@> @=cli$_locpres@>=@"0003FD31;@/ @:cli\$_locpres@> @=cli$_negated@>=@"000381F8;@/ @:cli\$_negated@> @=cli$_present@>=@"0003FD19;@/ @:cli\$_present@> @ @^system dependencies@> @= function@= cli$present@>( @:cli\$present@> @=%stdescr @>@=name@>:packed array[l..u:integer] of char):integer; external;@t\2@> function@= cli$get_value@>( @:cli\$get_value@> @=%stdescr @>@=name@>:packed array[l1..u1:integer] of char; @=%stdescr @>retbuf:packed array[l2..u2:integer] of char):integer; external;@t\2@> @ Check for the presence of a command line argument; return 1 if the argument is specified, 0 if the argument is not present, and -1 if the argument is explicitly negated. If the argument is present and has a value, place its value in |arg| and set |arg_len| to its length. @^system dependencies@> @= function get_arg (@!nam:packed array[l..u:integer] of char; @!has_val:boolean):integer; label @!done,@!exit; var @!status:integer; k:0..arg_size; begin get_arg:=0; arg_len:=0;@/ status:=@=cli$present@>(nam); @:cli\$present@> if (status=@=cli$_negated@>) or (status=@=cli$_locneg@>) then @:cli\$_negated@>@:cli\$_locneg@> get_arg:=-1 else if (status=@=cli$_concat@>) or (status=@=cli$_defaulted@>) or @:cli\$_concat@>@:cli\$_defaulted@> (status=@=cli$_present@>) or (status=@=cli$_locpres@>) then @:cli\$_present@>@:cli\$_locpres@> begin if has_val then begin if not odd(@=cli$get_value@>(nam,arg)) then return; @:cli\$get_value@> arg_len:=arg_size; while arg_len>0 do if arg[arg_len]=' ' then decr(arg_len) @+else goto done; done: end; get_arg:=1; end; exit: end; @ The following procedures initialize the various RMS control blocks. The processing is similar for all block types; the code clears the block to zero, then sets the block id and block length fields. @^system dependencies@> @= procedure init_fab(var @!f:@=fab$type@>); @:fab\$type@> type @!bytearray = packed array[1..@=fab$c_bln@>] of 0..255; @:fab\$c_bln@> var @!i:integer; begin for i:=1 to@= fab$c_bln @>do f@=::@>bytearray[i]:=0; @:fab\$c_bln@> f.@=fab$b_bid@>:=@=fab$c_bid@>; @:fab\$b_bid@>@:fab\$c_bid@> f.@=fab$b_bln@>:=@=fab$c_bln@>; @:fab\$b_bln@>@:fab\$c_bln@> end; @# procedure init_nam(var @!n:@=nam$type@>); @:nam\$type@> type @!bytearray = packed array[1..@=nam$c_bln@>] of 0..255; @:nam\$c_bln@> var @!i:integer; begin for i:=1 to@= nam$c_bln @>do n@=::@>bytearray[i]:=0; @:nam\$c_bln@> n.@=nam$b_bid@>:=@=nam$c_bid@>; @:nam\$b_bid@>@:nam\$c_bid@> n.@=nam$b_bln@>:=@=nam$c_bln@>; @:nam\$b_bln@>@:nam\$c_bln@> end; @ Augment the string currently in |arg| to produce a complete file specification; produce an output parse if |out_parse| is true, otherwise an input file. Use as default the file type passed as |ext|. If |scrounge| is true, use the principal input file, |tex_file|, for additional defaults. @^system dependencies@> @= procedure add_defaults(@!ext:packed array[l..u:integer] of char; @!scrounge,@!out_parse:boolean); var @!fab:@=fab$type@>; @!nam:@=nam$type@>; @!def,@!tmp:arg_buf; @:fab\$type@>@:nam\$type@> f:fab_pointer; i:0..arg_size; begin for i:=arg_len+1 to arg_size do arg[i]:=' '; for i:=1 to arg_size do def[i]:=' '; def[1]:='.'; for i:=1 to@= length@>(ext) do def[i+1]:=ext[i]; for i:=1 to arg_size do tmp[i]:=' '; init_fab(fab); init_nam(nam);@/ fab.@=fab$l_dna@>:=iaddress(def); fab.@=fab$b_dns@>:=arg_size;@/ @:fab\$l_dna@>@:fab\$b_dns@> fab.@=fab$l_fna@>:=iaddress(arg); fab.@=fab$b_fns@>:=arg_size;@/ @:fab\$l_fna@>@:fab\$b_fns@> fab.@=fab$l_nam@>:=iaddress(nam);@/ @:fab\$l_nam@> nam.@=nam$l_esa@>:=iaddress(tmp); nam.@=nam$b_ess@>:=arg_size;@/ @:nam\$l_esa@>@:nam\$b_ess@> if out_parse then fab.@=fab$l_fop@>:=@=fab$m_ofp@>;@/ @:fab\$l_fop@>@:fab\$m_ofp@> if scrounge then begin f:=@=pas$fab@>(tex_file); nam.@=nam$l_rlf@>:=f^.@=fab$l_nam@>; @:pas\$fab@>@:nam\$l_rlf@>@:fab\$l_nam@> end; if odd(@=$parse@>(fab)) then @:\$parse@> begin arg:=tmp; arg_len:=nam.@=nam$b_esl@>; @:nam\$b_esl@> end; end; @ The |@=$exit@>| system service passes a program status value @:\$exit@> back to whoever invoked \.{SCANWEAVE}; we set |@=sts$m_inhib_msg@>| to keep error messages from appearing. @:sts\$m_inhib_msg@> It would be more in keeping with VAX/VMS conventions to use |@=lib$stop@>| @:lib\$stop@> to report error conditions (allowing exception handlers to gain control before the program exits), but setting the |@=sts$m_inhib_msg@>| causes the @:sts\$m_inhib_msg@> \pv\ default exception handler to reset the status value to |@=sts$k_normal@>|. @:sts\$k_normal@> @^system dependencies@> @= begin if history=harmless_message then @= $exit@>(@=sts$k_warning@>+@=sts$m_inhib_msg@>) @:\$exit@>@:sts\$k_warning@>@:sts\$m_inhib_msg@> else if history=error_message then @= $exit@>(@=sts$k_error@>+@=sts$m_inhib_msg@>) @:\$exit@>@:sts\$k_error@>@:sts\$m_inhib_msg@> else if history=fatal_message then @= $exit@>(@=sts$k_severe@>+@=sts$m_inhib_msg@>); @:\$exit@>@:sts\$k_severe@>@:sts\$m_inhib_msg@> end @ The following code collects command line arguments from VAX/VMS, and connects external files to |tex_file|, |out_tex_file| and |out_change_file|. It resets the input files just to make sure they are accessible; they are reset again at the beginning of the first pass. @^system dependencies@> @d add_input_defaults(#)==add_defaults(#,false) @d add_output_defaults(#)==add_defaults(#,true) @d setup_error(#) == begin {report error during command argument processing} new_line; print(#,' ',arg:arg_len); mark_fatal; jump_out; end @= if get_arg('tex_file',true)<=0 then setup_error('! No WEB file specified'); @.No WEB file specified@> add_input_defaults('TeX',false);@/ @=open@>(tex_file,arg,@=readonly@>,@=error@>:=@=continue@>); if status(tex_file)<=0 then reset(tex_file,@=error@>:=@=continue@>); if status(tex_file)>0 then setup_error('! Can''t open TeX file'); @.Can't open TeX file@> if get_arg('out_tex_file',true)<=0 then do_nothing; add_output_defaults('outTeX',true);@/ @=open@>(out_tex_file,arg,@=new@>,@=disposition@>:=@=delete@>, @=error@>:=@=continue@>); if status(out_tex_file)<=0 then rewrite(out_tex_file,@=error@>:=@=continue@>); if status(out_tex_file)>0 then setup_error('! Can''t create output TeX file '); @.Can't create output TeX file@> @z