IMPLEMENTATION MODULE Options; (* Author: Andrew Trevorrow Implementation: University of Hamburg Modula-2 under VAX/VMS version 4 Date Started: August, 1986 Description: This module uses CLI routines to extract the DVI file parameter and qualifier values from the PSDVI command line. Revised: November, 1987 (while at The Open University) - Added /TFM_DIRECTORY and /CONSERVE_VM qualifiers. June--August, 1988 (while at Aston University) - Added /PSPREFIX, /INCREMENT, /HOFFSET and /VOFFSET qualifiers. *) FROM CommandLanguageInterface IMPORT CLI$PRESENT, CLI$GET_VALUE; FROM Conversions IMPORT StringToCard, StringToReal, Done; FROM TermOut IMPORT Write, WriteString, WriteLn, Halt; CONST NULL = 0C; (* SYSDEP: terminates a string *) VAR value : stringvalue; (* temporary string *) (******************************************************************************) PROCEDURE GetDVIFile; (* Get DVI file name from command line. *) VAR i, status : CARDINAL; BEGIN DVIname := ''; status := CLI$GET_VALUE('FILESPEC',DVIname); (* CLD ensures it is there *) i := HIGH(DVIname); WHILE (i > 0) AND (DVIname[i] = ' ') DO (* remove trailing blanks *) DVIname[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; IF DVIname[i] = ':' THEN (* assume logical name *) (* no need to translate *) ELSE INC(i); (* = LEN(DVIname) *) IF NOT ExplicitExt(DVIname) THEN (* append .DVI *) IF i + 3 <= HIGH(DVIname) THEN DVIname[i] := '.'; DVIname[i+1] := 'D'; DVIname[i+2] := 'V'; DVIname[i+3] := 'I'; ELSE (* user has given a mighty long file spec! *) WriteString('DVI file specification is too long!'); WriteLn; WriteString(DVIname); WriteLn; Halt(2); END; END; END; (* bad DVIname will be detected upon open in main module *) END GetDVIFile; (******************************************************************************) PROCEDURE ExplicitExt (fname : ARRAY OF CHAR) : BOOLEAN; (* SYSDEP: VAX/VMS files have an extension of the form ".xxx", also known as the file type. If given file specification contains an extension then TRUE is returned, otherwise FALSE. *) VAR pos : CARDINAL; ch : CHAR; BEGIN pos := LEN(fname); WHILE pos > 0 DO (* search backwards looking for . or : or ] *) DEC(pos); ch := fname[pos]; IF ch = '.' THEN RETURN TRUE ELSIF (ch = ':') OR (ch = ']') THEN (* don't need to look further *) RETURN FALSE END; END; RETURN FALSE; END ExplicitExt; (******************************************************************************) PROCEDURE GetCardinal (qualifier : ARRAY OF CHAR; VAR n : CARDINAL); (* Check if qualifier is present. If so, then make sure given value is a positive integer, and return via n. If not present then return 0. *) VAR i, status : CARDINAL; BEGIN IF ODD(CLI$PRESENT(qualifier)) THEN (* CLD ensures it has a value *) status := CLI$GET_VALUE(qualifier,value); i := HIGH(value); WHILE (i > 0) AND (value[i] = ' ') DO (* remove trailing blanks *) value[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; n := StringToCard(value); IF Done() AND (n > 0) THEN (* return *) ELSE WriteString('Bad /'); WriteString(qualifier); WriteString(' value: '); WriteString(value); WriteLn; WriteString('Specify a positive integer.'); WriteLn; Halt(2); END; ELSE n := 0; (* qualifier not present *) END; END GetCardinal; (******************************************************************************) PROCEDURE GetPosDimension (qualifier : ARRAY OF CHAR; VAR pixels : CARDINAL); (* Check if qualifier is present. If so, then make sure given value is a valid positive dimension, convert and return via pixels. A valid positive dimension consists of a positive integer or real number followed by a two-letter unit in any case. *) VAR i, status : CARDINAL; r : REAL; ch1, ch2 : CHAR; units : validunits; BEGIN IF ODD(CLI$PRESENT(qualifier)) THEN (* CLD ensures it has a value *) status := CLI$GET_VALUE(qualifier,value); i := HIGH(value); WHILE (i > 0) AND (value[i] = ' ') DO (* remove trailing blanks *) value[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; IF i = 0 THEN i := 1 END; (* extract units *) IF (Cap(value[i-1]) = 'I') AND (Cap(value[i]) = 'N') THEN units := in; ELSIF (Cap(value[i-1]) = 'C') AND (Cap(value[i]) = 'M') THEN units := cm; ELSIF (Cap(value[i-1]) = 'M') AND (Cap(value[i]) = 'M') THEN units := mm; ELSIF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'C') THEN units := pc; ELSIF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'T') THEN units := pt; ELSIF (Cap(value[i-1]) = 'B') AND (Cap(value[i]) = 'P') THEN units := bp; ELSIF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'X') THEN units := px; ELSE WriteString('Bad units in /'); WriteString(qualifier); WriteString(' dimension: '); WriteString(value); WriteLn; WriteString('Last two letters should be IN, CM, MM, PC, PT, BP or PX.'); WriteLn; Halt(2); END; ch1 := value[i-1]; (* remember letters in units *) ch2 := value[i]; value[i] := NULL; (* remove units *) value[i-1] := NULL; r := StringToReal(value); IF Done() AND (r > 0.0) THEN (* convert r to pixels *) CASE units OF in : pixels := TRUNC(r * FLOAT(resolution) + 0.5) | cm : pixels := TRUNC((r / 2.54) * FLOAT(resolution) + 0.5) | mm : pixels := TRUNC((r / 25.4) * FLOAT(resolution) + 0.5) | pc : pixels := TRUNC((r / 72.27) * 12.0 * FLOAT(resolution) + 0.5) | pt : pixels := TRUNC((r / 72.27) * FLOAT(resolution) + 0.5) | bp : pixels := TRUNC((r / 72.0) * FLOAT(resolution) + 0.5) | px : pixels := TRUNC(r + 0.5) END; ELSE value[i-1] := ch1; (* restore units *) value[i] := ch2; WriteString('Bad /'); WriteString(qualifier); WriteString(' value: '); WriteString(value); WriteLn; WriteString('Specify a positive dimension.'); WriteLn; Halt(2); END; ELSE pixels := 0; (* qualifier not present *) END; END GetPosDimension; (******************************************************************************) PROCEDURE GetDimension (qualifier : ARRAY OF CHAR; VAR pixels : INTEGER); (* Check if qualifier is present. If so, then make sure given value is a valid dimension, convert and return via pixels. A valid dimension consists of an integer or real number (possibly negative) followed by a two-letter unit in any case. *) VAR i, status : CARDINAL; r : REAL; ch1, ch2 : CHAR; units : validunits; BEGIN IF ODD(CLI$PRESENT(qualifier)) THEN (* CLD ensures it has a value *) status := CLI$GET_VALUE(qualifier,value); i := HIGH(value); WHILE (i > 0) AND (value[i] = ' ') DO (* remove trailing blanks *) value[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; IF i = 0 THEN i := 1 END; (* extract units *) IF (Cap(value[i-1]) = 'I') AND (Cap(value[i]) = 'N') THEN units := in; ELSIF (Cap(value[i-1]) = 'C') AND (Cap(value[i]) = 'M') THEN units := cm; ELSIF (Cap(value[i-1]) = 'M') AND (Cap(value[i]) = 'M') THEN units := mm; ELSIF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'C') THEN units := pc; ELSIF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'T') THEN units := pt; ELSIF (Cap(value[i-1]) = 'B') AND (Cap(value[i]) = 'P') THEN units := bp; ELSIF (Cap(value[i-1]) = 'P') AND (Cap(value[i]) = 'X') THEN units := px; ELSE WriteString('Bad units in /'); WriteString(qualifier); WriteString(' dimension: '); WriteString(value); WriteLn; WriteString('Last two letters should be IN, CM, MM, PC, PT, BP or PX.'); WriteLn; Halt(2); END; ch1 := value[i-1]; (* remember letters in units *) ch2 := value[i]; value[i] := NULL; (* remove units *) value[i-1] := NULL; r := StringToReal(value); IF Done() THEN (* convert r to pixels *) CASE units OF in : pixels := TRUNC(ABS(r) * FLOAT(resolution) + 0.5) | cm : pixels := TRUNC((ABS(r)/2.54) * FLOAT(resolution) + 0.5) | mm : pixels := TRUNC((ABS(r)/25.4) * FLOAT(resolution) + 0.5) | pc : pixels := TRUNC((ABS(r)/72.27) * 12.0 * FLOAT(resolution) + 0.5) | pt : pixels := TRUNC((ABS(r)/72.27) * FLOAT(resolution) + 0.5) | bp : pixels := TRUNC((ABS(r)/72.0) * FLOAT(resolution) + 0.5) | px : pixels := TRUNC(ABS(r) + 0.5) END; IF r < 0.0 THEN pixels := -pixels END; ELSE value[i-1] := ch1; (* restore units *) value[i] := ch2; WriteString('Bad /'); WriteString(qualifier); WriteString(' value: '); WriteString(value); WriteLn; WriteString('Specify a valid dimension.'); WriteLn; Halt(2); END; ELSE pixels := 0; (* qualifier not present *) END; END GetDimension; (******************************************************************************) PROCEDURE Cap (ch : CHAR) : CHAR; (* Hamburg's CAP is stupid; do my own. *) BEGIN IF (ch < 'a') OR (ch > 'z') THEN RETURN ch; ELSE RETURN CAP(ch); END; END Cap; (******************************************************************************) PROCEDURE GetString (qualifier : ARRAY OF CHAR; VAR s : ARRAY OF CHAR); (* Check if qualifier is present. If so, then get value and return via s. If qualifier not present then return empty string. *) VAR i, status : CARDINAL; BEGIN IF ODD(CLI$PRESENT(qualifier)) THEN (* CLD ensures it has a value *) status := CLI$GET_VALUE(qualifier,s); i := HIGH(s); WHILE (i > 0) AND (s[i] = ' ') DO (* remove trailing blanks *) s[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; ELSE s[0] := NULL; (* SYSDEP: LEN(s) will be 0 *) END; (* the main module will detect bad s value sooner or later *) END GetString; (******************************************************************************) PROCEDURE Append (VAR s1 : ARRAY OF CHAR; s2 : ARRAY OF CHAR); (* Append s2 to s1. *) VAR i, j : CARDINAL; BEGIN i := LEN(s1); (* SYSDEP: assumes s1 ends with NULL, unless full *) j := 0; WHILE (i <= HIGH(s1)) AND (j <= HIGH(s2)) AND (s2[j] <> NULL) DO s1[i] := s2[j]; INC(i); INC(j); END; (* DEBUG IF (i > HIGH(s1)) AND (j <= HIGH(s2)) AND (s2[j] <> NULL) THEN WriteString('No room to append '); WriteString(s2); WriteLn; Halt(2); END; GUBED *) IF i <= HIGH(s1) THEN s1[i] := NULL END; END Append; (******************************************************************************) PROCEDURE GetUnits; (* Check if /UNITS is present. If so, then make sure given value is valid and set units. *) VAR i, status : CARDINAL; ch1, ch2 : CHAR; BEGIN IF ODD(CLI$PRESENT('UNITS')) THEN (* CLD ensures it has a value *) status := CLI$GET_VALUE('UNITS',value); i := HIGH(value); WHILE (i > 0) AND (value[i] = ' ') DO (* remove trailing blanks *) value[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; ch1 := Cap(value[0]); ch2 := Cap(value[1]); IF (ch1 = 'I') AND (ch2 = 'N') THEN units := in; ELSIF (ch1 = 'C') AND (ch2 = 'M') THEN units := cm; ELSIF (ch1 = 'M') AND (ch2 = 'M') THEN units := mm; ELSIF (ch1 = 'P') AND (ch2 = 'C') THEN units := pc; ELSIF (ch1 = 'P') AND (ch2 = 'T') THEN units := pt; ELSIF (ch1 = 'B') AND (ch2 = 'P') THEN units := bp; ELSIF (ch1 = 'P') AND (ch2 = 'X') THEN units := px; ELSE WriteString('Bad /UNITS value: '); WriteString(value); WriteLn; WriteString('Specify IN, CM, MM, PC, PT, BP or PX.'); WriteLn; Halt(2); END; ELSE units := px; (* if /UNITS not present *) END; END GetUnits; (******************************************************************************) PROCEDURE GetPages; (* Check if /PAGES is present. If so then subrange will be TRUE. /PAGES can accept 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 = MAX(CARDINAL). 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). *) VAR i, j, status, len : CARDINAL; BEGIN IF ODD(CLI$PRESENT('PAGES')) THEN (* CLD ensures it has a value *) status := CLI$GET_VALUE('PAGES',value); i := HIGH(value); WHILE (i > 0) AND (value[i] = ' ') DO (* remove trailing blanks *) value[i] := NULL; (* SYSDEP: pad with NULLs *) DEC(i); END; len := i + 1; (* length of value *) firstTeXpage := ''; finalTeXpage := ''; i := 0; WHILE (i < len) AND (value[i] <> ':') DO (* extract first page *) firstTeXpage[i] := value[i]; INC(i); END; IF value[0] = ':' THEN (* first page not given *) firstDVIpage := 1; ELSIF firstTeXpage[0] = '[' THEN (* TeX page given *) firstDVIpage := 0; ELSE (* DVI page given *) firstDVIpage := StringToCard(firstTeXpage); IF NOT Done() OR (firstDVIpage = 0) THEN WriteString('/PAGES error! Bad first page: '); WriteString(firstTeXpage); WriteLn; Halt(2); END; END; IF i = len THEN (* no colon; /PAGES=n or [t] *) IF firstTeXpage[0] = '[' THEN finalTeXpage := firstTeXpage; (* [t] = [t]:[t] *) finalDVIpage := 0; ELSE finalDVIpage := firstDVIpage; (* n = n:n *) END; ELSE (* value[i] = ':' *) INC(i); j := 0; WHILE i < len DO (* extract final page *) finalTeXpage[j] := value[i]; INC(i); INC(j); END; IF j = 0 THEN (* no page after ':' *) finalDVIpage := MAX(CARDINAL); ELSIF finalTeXpage[0] = '[' THEN (* TeX page given *) finalDVIpage := 0; ELSE (* DVI page given *) finalDVIpage := StringToCard(finalTeXpage); IF NOT Done() OR (finalDVIpage = 0) THEN WriteString('/PAGES error! Bad final page: '); WriteString(finalTeXpage); WriteLn; Halt(2); END; END; END; subrange := TRUE; (* main module will check page range *) ELSE subrange := FALSE; (* if /PAGES not present *) END; END GetPages; (******************************************************************************) (* SYSDEP: CLD file must supply some qualifiers with default values. *) BEGIN GetDVIFile; (* initialize DVIname *) GetString('OUTPUT',PSname); (* initialize PSname *) IF LEN(PSname) = 0 THEN (* /OUTPUT not specified *) PSname := 'OUT.PS'; (* It would be nicer to use DVIname with .PS instead of .DVI but things get messy if DVIname is a logical name or includes a directory. Note that PSPRINT.COM specifies an explicit /OUTPUT file. *) END; GetCardinal('MAGNIFICATION',mag); (* 0 if no /MAG override *) GetCardinal('RESOLUTION',resolution); (* get resolution BEFORE dimens *) GetPosDimension('XSIZE',paperwd); GetPosDimension('YSIZE',paperht); GetDimension('HOFFSET',hoffset); (* 0 if not given *) GetDimension('VOFFSET',voffset); (* ditto *) GetString('HEADER',header); (* empty string if no /HEADER *) GetString('PSPREFIX',psprefix); GetString('TFM_DIRECTORY',tfmdir); GetString('FONT_DIRECTORY',fontdir); GetString('DUMMY_FONT',value); dummyfont := fontdir; (* prefix dummyfont with fontdir *) Append(dummyfont,value); GetUnits; (* initialize units *) GetPages; (* initialize subrange etc. *) GetCardinal('INCREMENT',increment); (* 0 if /INC not used *) IF increment = 0 THEN increment := 1 END; (* do normal page selection *) stats := ODD(CLI$PRESENT('STATS')); reverse := ODD(CLI$PRESENT('REVERSE')); conserveVM := ODD(CLI$PRESENT('CONSERVE_VM')); END Options.