(* This module initializes the DVI file parameter and command line options.
   Highly SYSDEP!
*)

#include 'globals.h';
#include 'options.h';

VAR
   option : CHAR;       (* current command option *)
   value : string;      (* current option's value *)
   vlength : INTEGER;   (* current value's length *)
   argnum : INTEGER;    (* 0..argc-1; used in argv calls *)

FUNCTION sscanf (VAR s, format : string; VAR r : real) : integer;
EXTERNAL;

(******************************************************************************)

FUNCTION Cap (ch : CHAR) : CHAR;

(* If ch is in 'a'..'z' then return capitalized letter else return ch. *)

BEGIN
IF (ch < 'a') OR (ch > 'z') THEN Cap := ch ELSE Cap := CHR(ORD(ch) - 32);
END; (* Cap *)

(******************************************************************************)

FUNCTION Len (str : string) : INTEGER;

(* Return length of given string, assumed to be padded with blanks. *)

LABEL 888;

VAR i : INTEGER;

BEGIN
i := maxstring;
WHILE i > 0 DO BEGIN
   IF str[i-1] <> ' ' THEN goto 888;
   i := i - 1;
END;
888:
Len := i;
END; (* Len *)

(******************************************************************************)

FUNCTION ExplicitExt (fname : string; len : INTEGER) : BOOLEAN;

(* SYSDEP: Check for an extension of the form ".*" where * is any string
   not containing "/".  len is length of fname.
   If "." found then TRUE is returned, otherwise FALSE.
*)

LABEL 999;

BEGIN
WHILE len > 0 DO BEGIN   (* search backwards looking for . *)
   len := len - 1;
   IF fname[len] = '/' THEN BEGIN   (* handle file names like ../myfiles/foo *)
      ExplicitExt := FALSE;
      goto 999;
   END
   ELSE IF fname[len] = '.' THEN BEGIN
      ExplicitExt := TRUE;
      goto 999;
   END;
END;
ExplicitExt := FALSE;
999:
END; (* ExplicitExt *)

(******************************************************************************)

PROCEDURE GetValue;

(* Get parameter following current option and store in value. *)

VAR i : integer;

BEGIN
IF vlength > 2 THEN BEGIN                      (* allow things like -m1000 *)
   FOR i := 0 TO vlength - 1 DO
      value[i] := value[i+2];                  (* shift value left 2 places *)
   vlength := vlength - 2;
END
ELSE BEGIN
   (* option should be followed by value *)
   value := ' ';
   IF argnum < argc THEN argv(argnum,value);
   vlength := Len(value);                      (* 0 if no more args *)
   argnum := argnum + 1;
   IF vlength <= 0 THEN BEGIN
      writeln('Missing value after -',option);
      exit(1);
   END;
END;
END; (* GetValue *)

(******************************************************************************)

PROCEDURE GetCardinal (VAR n : INTEGER);

(* If current value represents a positive integer then return via n. *)

VAR result : integer;   r : real;   fmt : string;

BEGIN
fmt := '%f';
result := sscanf(value,fmt,r);
n := trunc(r);
IF (ABS(result) <> 1) OR (n <= 0) THEN BEGIN
   writeln('Bad -',option,' value: ',value:vlength);
   writeln('Specify a positive integer.');
   exit(1);
END;
END; (* GetCardinal *)

(******************************************************************************)

PROCEDURE GetPosDimen (VAR r : REAL; VAR un : validunits);

(* A valid +ve dimension consists of a positive integer or real number followed
   by a two-letter unit: cm, mm, in, pc, pt, bp or px (or in uppercase).
   If current value represents a valid +ve dimension, we return number part
   in r and validunits part in un.
*)

VAR i, result : INTEGER;   ch1, ch2 : CHAR;   fmt : string;

BEGIN
(* extract un *)
IF vlength > 1 THEN i := vlength-1 ELSE i := 1;
IF      (Cap(value[i-1]) = 'I') AND (Cap(value[i]) = 'N') THEN
   un := ic
ELSE IF (Cap(value[i-1]) = 'C') AND (Cap(value[i]) = 'M') THEN
   un := cm
ELSE IF (Cap(value[i-1]) = 'M') AND (Cap(value[i]) = 'M') THEN
   un := mm
ELSE IF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'C') THEN
   un := pc
ELSE IF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'T') THEN
   un := pt
ELSE IF (Cap(value[i-1]) = 'B') AND (Cap(value[i]) = 'P') THEN
   un := bp
ELSE IF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'X') THEN
   un := px
ELSE BEGIN
   writeln('Bad units in -',option,' value: ',value:vlength);
   writeln('Last two letters should be cm, mm, in, pc, pt, bp or px.');
   exit(1);
END;
ch1 := value[i-1];                           (* remember letters in units *)
ch2 := value[i];
value[i]   := ' ';                           (* remove units *)
value[i-1] := ' ';
fmt := '%f';
result := sscanf(value,fmt,r);
IF (ABS(result) <> 1) OR (r <= 0.0) THEN BEGIN
   value[i-1] := ch1;                        (* restore units *)
   value[i]   := ch2;
   writeln('Bad -',option,' value: ',value:vlength);
   writeln('Specify a positive dimension.');
   exit(1);
END;
END; (* GetPosDimen *)

(******************************************************************************)

PROCEDURE GetDimen (VAR r : REAL; VAR un : validunits);

(* A valid dimension consists of an integer or real number followed
   by a two-letter unit: cm, mm, in, pc, pt, bp or px (or in uppercase).
   If current value represents a valid dimension, we return number part
   in r and validunits part in un.
*)

VAR i, result : INTEGER;   ch1, ch2 : CHAR;   fmt : string;

BEGIN
(* extract un *)
IF vlength > 1 THEN i := vlength-1 ELSE i := 1;
IF      (Cap(value[i-1]) = 'I') AND (Cap(value[i]) = 'N') THEN
   un := ic
ELSE IF (Cap(value[i-1]) = 'C') AND (Cap(value[i]) = 'M') THEN
   un := cm
ELSE IF (Cap(value[i-1]) = 'M') AND (Cap(value[i]) = 'M') THEN
   un := mm
ELSE IF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'C') THEN
   un := pc
ELSE IF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'T') THEN
   un := pt
ELSE IF (Cap(value[i-1]) = 'B') AND (Cap(value[i]) = 'P') THEN
   un := bp
ELSE IF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'X') THEN
   un := px
ELSE BEGIN
   writeln('Bad units in -',option,' value: ',value:vlength);
   writeln('Last two letters should be cm, mm, in, pc, pt, bp or px.');
   exit(1);
END;
ch1 := value[i-1];                           (* remember letters in units *)
ch2 := value[i];
value[i]   := ' ';                           (* remove units *)
value[i-1] := ' ';
fmt := '%f';
result := sscanf(value,fmt,r);
IF (ABS(result) <> 1) THEN BEGIN
   value[i-1] := ch1;                        (* restore units *)
   value[i]   := ch2;
   writeln('Bad -',option,' value: ',value:vlength);
   writeln('Specify a dimension.');
   exit(1);
END;
END; (* GetDimen *)

(******************************************************************************)

PROCEDURE GetUnits;

(* Make sure current value is valid and set units if so. *)

VAR ch1, ch2 : CHAR;

BEGIN
ch1 := Cap(value[0]);
ch2 := Cap(value[1]);
IF      (ch1 = 'I') AND (ch2 = 'N') THEN
   units := ic
ELSE IF (ch1 = 'C') AND (ch2 = 'M') THEN
   units := cm
ELSE IF (ch1 = 'M') AND (ch2 = 'M') THEN
   units := mm
ELSE IF (ch1 = 'P') AND (ch2 = 'C') THEN
   units := pc
ELSE IF (ch1 = 'P') AND (ch2 = 'T') THEN
   units := pt
ELSE IF (ch1 = 'B') AND (ch2 = 'P') THEN
   units := bp
ELSE IF (ch1 = 'P') AND (ch2 = 'X') THEN
   units := px
ELSE BEGIN
   writeln('Bad -',option,' value: ',value:vlength);
   writeln('Specify cm, mm, in, pc, pt, bp or px.');
   exit(1);
END;
END; (* GetUnits *)

(******************************************************************************)

PROCEDURE GetPages;

(* Parse -p value.  If valid then subrange will become TRUE.
   -p can have any value of the form "first:final" where first and/or
   final can be a DVI page (positive integer), or TeX page ([i0. ... .i9]),
   or empty.  If first empty then firstDVIpage = 1; if final empty then
   finalDVIpage = maxint.  If ":final" is omitted then
   finalDVIpage = firstDVIpage.
   If first/final is a TeX page specification (i.e., starts with '[')
   then first/finalDVIpage is set to 0 and first/finalTeXpage contains the
   given string (and parsing is done by the main module).
*)

LABEL 888;

VAR i, j, result : INTEGER;   r : real;   fmt : string;

BEGIN
subrange := TRUE;
firstTeXpage := ' ';
finalTeXpage := ' ';
i := 0;
(* extract first page *)
WHILE i < vlength DO BEGIN
   IF value[i] = ':' THEN goto 888;
   firstTeXpage[i] := value[i];
   i := i + 1;
END;
888:
IF value[0] = ':' THEN                         (* first page not given *)
   firstDVIpage := 1
ELSE IF firstTeXpage[0] = '[' THEN             (* TeX page given *)
   firstDVIpage := 0
ELSE BEGIN                                     (* DVI page given *)
   fmt := '%f';
   result := sscanf(firstTeXpage,fmt,r);
   firstDVIpage := trunc(r);
   IF (ABS(result) <> 1) OR (firstDVIpage <= 0) THEN BEGIN
      writeln('Bad first page in -',option,' value: ',value:vlength);
      writeln('Specify a positive integer.');
      exit(1);
   END;
END;
IF i = vlength THEN BEGIN                      (* no colon; n or [t] *)
   IF firstTeXpage[0] = '[' THEN BEGIN
      finalTeXpage := firstTeXpage;            (* [t] = [t]:[t] *)
      finalDVIpage := 0;
   END
   ELSE
      finalDVIpage := firstDVIpage;            (* n = n:n *)
END
ELSE BEGIN                                     (* value[i] = ':' *)
   i := i + 1;
   j := 0;
   WHILE i < vlength DO BEGIN                  (* extract final page *)
      finalTeXpage[j] := value[i];
      i := i + 1;
      j := j + 1;
   END;
   IF j = 0 THEN                               (* no page after ':' *)
      finalDVIpage := maxint
   ELSE IF finalTeXpage[0] = '[' THEN          (* TeX page given *)
      finalDVIpage := 0
   ELSE BEGIN                                  (* DVI page given *)
      fmt := '%f';
      result := sscanf(finalTeXpage,fmt,r);
      finalDVIpage := trunc(r);
      IF (ABS(result) <> 1) OR (finalDVIpage <= 0) THEN BEGIN
         writeln('Bad final page in -',option,' value: ',value:vlength);
         writeln('Specify a positive integer.');
         exit(1);
      END;
   END;
END;
END; (* GetPages *)

(******************************************************************************)

FUNCTION DimenPixels (r : REAL; u : validunits) : INTEGER;

(* Return given dimension in terms of pixels. *)

BEGIN
CASE u OF
   ic : DimenPixels := TRUNC(r * resolution + 0.5);
   cm : DimenPixels := TRUNC((r / 2.54) * resolution + 0.5);
   mm : DimenPixels := TRUNC((r / 25.4) * resolution + 0.5);
   bp : DimenPixels := TRUNC((r / 72.0) * resolution + 0.5);
   pt : DimenPixels := TRUNC((r / 72.27) * resolution + 0.5);
   pc : DimenPixels := TRUNC((r / 72.27) * 12.0 * resolution + 0.5);
   px : DimenPixels := TRUNC(r + 0.5)
END;
END; (* DimenPixels *)

(******************************************************************************)

PROCEDURE InitOptions;

(* Get DVI file and any options from command line.
   If an option appears more than once then we return the last value.
*)

VAR
   hoffu, voffu, xu, yu : validunits;
   hoffr, voffr, xr, yr : REAL;
   landscape : boolean;
   temp, strlen, i : integer;
   str : string;

BEGIN
(* initialize option values with defaults; note that the psprint script can
   set up different defaults
*)
resolution := 300;                           (* LaserWriter resolution *)
hoffu      := ic;
voffu      := ic;
hoffr      := 0.0;                           (* no margin shifting *)
voffr      := 0.0;
xu         := ic;                            (* paper dimensions in inches *)
yu         := ic;
xr         := 8.3;                           (* A4 paper is 8.3" wide *)
yr         := 11.7;                          (* A4 paper is 11.7" high *)
mag        := 0;                             (* use DVI mag *)
fontdir    := '/tex/pk/';                    (* location of PK files *)
dummyfont  := 'cmr10.300pk';                 (* typical font *)
tfmdir     := '/tex/fonts/';                 (* location of PS TFM files *)
psprefix   := 'ps-';                         (* prefix in PS font names *)
DVIname    := ' ';                           (* SYSDEP: empty string *)
(* above are same as DVItoVDU options *)
header       := '/usr/users/applied/staff/atrevorr/psdvi/tex.ps';
PSname       := 'out.ps';                    (* output file (-o) *)
units        := ic;                          (* inches (-u) *)
stats        := FALSE;                       (* display stats? (-s) *)
reverse      := FALSE;                       (* reverse page order? (-b) *)
conserveVM   := FALSE;                       (* conserve VM? (-c) *)
landscape    := FALSE;                       (* reverse -x and -y? (-l) *)
subrange     := FALSE;                       (* -p value given? *)
firstDVIpage := 0;
finalDVIpage := 0;
firstTeXpage := '[]';
finalTeXpage := '[]';
argnum := 1;                                 (* 0 is command *)
WHILE argnum < argc DO BEGIN
   value := ' ';
   argv(argnum,value);
   vlength := Len(value);
   argnum := argnum + 1;
   IF value[0] = '-' THEN BEGIN
      IF vlength > 1 THEN option := value[1] ELSE option := ' ';
      CASE option OF
        'r' : BEGIN GetValue; GetCardinal(resolution); END;
        'm' : BEGIN GetValue; GetCardinal(mag);        END;
        'i' : BEGIN GetValue; GetCardinal(increment);  END;
        'x' : BEGIN GetValue; GetPosDimen(xr,xu);      END;
        'y' : BEGIN GetValue; GetPosDimen(yr,yu);      END;
        'H' : BEGIN GetValue; GetDimen(hoffr,hoffu);   END;
        'V' : BEGIN GetValue; GetDimen(voffr,voffu);   END;
        'T' : BEGIN GetValue; tfmdir    := value;      END;
        'P' : BEGIN GetValue; psprefix  := value;      END;
        'd' : BEGIN GetValue; dummyfont := value;      END;
        'f' : BEGIN GetValue; fontdir   := value;      END;
        'h' : BEGIN GetValue; header    := value;      END;
        'o' : BEGIN GetValue; PSname    := value;      END;
        'u' : BEGIN GetValue; GetUnits;                END;
        'p' : BEGIN GetValue; GetPages;                END;
        's' : stats      := TRUE;
        'b' : reverse    := TRUE;
        'c' : conserveVM := TRUE;
        'l' : landscape  := TRUE;
              (* bad string values will be detected in other modules *)
      OTHERWISE
         writeln('Unknown option: -',option);
         exit(1);
      END;
   END
   ELSE BEGIN
      (* value doesn't start with '-', so assume it is DVI file *)
      DVIname := value;
      IF NOT ExplicitExt(DVIname,vlength) THEN     (* append .dvi *)
         IF vlength + 3 < maxstring THEN BEGIN
            DVIname[vlength]   := '.';
            DVIname[vlength+1] := 'd';
            DVIname[vlength+2] := 'v';
            DVIname[vlength+3] := 'i';
         END
         ELSE BEGIN  (* user has given a mighty long file name *)
            writeln('DVI file name too long: ',DVIname:maxstring);
            exit(1);
         END;
      (* bad DVIname will be detected upon open in main module *)
   END;
END;
IF DVIname[0] = ' ' THEN BEGIN  (* no file name on command line *)
   writeln('DVI file not given!');
   exit(1);
END;
(* prepend fontdir to dummyfont *)
str := fontdir;
strlen := Len(fontdir);
FOR i := 1 TO Len(dummyfont) DO BEGIN
   IF strlen < maxstring THEN BEGIN
      str[strlen] := dummyfont[i-1];
      strlen := strlen + 1;
   END;
END;
dummyfont := str;
(* set h/voffset and paperwd/ht only after resolution has been decided *)
hoffset := DimenPixels(hoffr,hoffu);
voffset := DimenPixels(voffr,voffu);
paperwd := DimenPixels(xr,xu);
paperht := DimenPixels(yr,yu);
IF landscape THEN BEGIN   (* swap paperwd and paperht *)
   temp := paperwd;
   paperwd := paperht;
   paperht := temp;
END;
IF increment = 0 THEN increment := 1;   (* do normal page selection *)
END; (* InitOptions *)
