aboutsummaryrefslogtreecommitdiffstats
path: root/prg2lout.c
diff options
context:
space:
mode:
Diffstat (limited to 'prg2lout.c')
-rw-r--r--prg2lout.c1832
1 files changed, 1832 insertions, 0 deletions
diff --git a/prg2lout.c b/prg2lout.c
new file mode 100644
index 0000000..4df2187
--- /dev/null
+++ b/prg2lout.c
@@ -0,0 +1,1832 @@
+/*****************************************************************************/
+/* */
+/* PROG2LOUT: A PROGRAM TO CONVERT PROGRAM SOURCES INTO LOUT (VERSION 1.0) */
+/* COPYRIGHT (C) 2000 Jeffrey H. Kingston */
+/* */
+/* Jeffrey H. Kingston (jeff@cs.su.oz.au) */
+/* Basser Department of Computer Science */
+/* The University of Sydney 2006 */
+/* AUSTRALIA */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2, or (at your option) */
+/* any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* */
+/*****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+
+/*****************************************************************************/
+/* */
+/* How to add another language to prg2lout's list */
+/* */
+/* Step 1. Have a browse through the token declarations below, and work */
+/* out which of them you need for your language. If you need a token that */
+/* isn't there already, you'll have to define it; there are many examples */
+/* there to help you. */
+/* */
+/* Step 2. Browse through the language declarations, and declare your */
+/* language following those examples: first you give a set of one or more */
+/* alternative names for your language, then the default printing style for */
+/* it (either "fixed", "varying", or "symbol"), then you list the tokens */
+/* of the language, then you list the keywords. */
+/* */
+/* Step 3. Add your language variable to the list in the initializer of */
+/* variable languages, as you can see the others have been done. */
+/* */
+/* Step 4. If any lists of initializers now contain more than */
+/* MAX_ARRAY_LENGTH-1 elements, increase MAX_ARRAY_LENGTH until they don't. */
+/* */
+/* Step 5. Recompile and reinstall prg2lout, test "prg2lout -u" then */
+/* "prg2lout -l <mylanguage> <myfile> | lout -s > out.ps". */
+/* */
+/* Step 6. Send your tested and verified changes to jeff@cs.usyd.edu.au */
+/* for incorporation in the next Lout release. If you do this, please */
+/* try hard to ensure that your changes conform to the formal definition */
+/* of your language. */
+/* */
+/*****************************************************************************/
+
+
+/*****************************************************************************/
+/* */
+/* Character sets */
+/* */
+/* Here are prg2lout's definitions of various commonly needed sets of */
+/* characters. May need enhancement for Latin1 etc. */
+/* */
+/*****************************************************************************/
+
+char AllPrintable[] =
+ " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^_`\\{|}~\
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
+
+char AllPrintablePlusNL[] =
+ " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^_`\\{|}~\
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n" ;
+
+char AllPrintableTabNL[] =
+ " !\"#$%&'()*+,-./0123456789:;<=>?@[\\]^_`\\{|}~\
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\t" ;
+
+char NotDoubleQuote[] =
+ " !#$%&'()*+,-./0123456789:;<=>?@[\\]^_`\\{|}~\
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
+
+char NotQuote[] =
+ " !#$%&\"()*+,-./0123456789:;<=>?@[\\]^_`\\{|}~\
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
+
+char Letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
+
+char Letter_Digit[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789" ;
+
+#define SepLetters \
+"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", \
+"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", \
+"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", \
+"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
+
+#define SepDigits "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+
+
+/*****************************************************************************/
+/* */
+/* TOKEN - put your token declarations in this section */
+/* */
+/*****************************************************************************/
+#define MAX_CHAR 256
+#define MAX_STRING_LENGTH 101
+#define MAX_ARRAY_LENGTH 101
+
+
+typedef struct token_rec {
+ char *name; /* the name of this token, e.g. "string" */
+ char *command; /* the Lout command for it, e.g. "@D" */
+ char *alternate_command; /* the alternate command for it, e.g. "@K" */
+ char *starts[MAX_ARRAY_LENGTH]; /* strings that start this token */
+ char *legal; /* legal characters in this token */
+ char *escape; /* the escape character e.g. "\\", or "" */
+ char *escape_legal; /* legal characters after escape character */
+ char *inner_escape; /* inner escape, e.g. "`", or else "" */
+ char *end_inner_escape; /* end inner escape, e.g. "'" */
+ char *end_delimiter; /* the end delimiter, else "" */
+
+ /* The following options are initialized by the program, so don't you */
+ char chtype[MAX_CHAR]; /* character types within token */
+ char escape_chtype[MAX_CHAR]; /* character types after escape */
+} TOKEN;
+
+
+TOKEN CStringToken = {
+ "string", /* used by error messages involving this token */
+ "@S", /* Lout command for formatting strings */
+ "", /* No alternate command */
+ { "\"" }, /* strings begin with a " character */
+ NotDoubleQuote, /* inside, any printable except " is OK */
+ "\\", /* within strings, \\ is the escape character */
+ AllPrintablePlusNL, /* after escape char, any printable char or nl OK */
+ "", /* strings do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "\"" /* strings end with a " character */
+};
+
+TOKEN CCharacterToken = {
+ "character", /* used by error messages involving this token */
+ "@C", /* Lout command for formatting characters */
+ "", /* No alternate command */
+ { "'" }, /* characters begin with a ' character */
+ NotQuote, /* inside, any printable except ' is OK */
+ "\\", /* within characters, \\ is the escape character */
+ AllPrintable, /* after escape char, any printable char is OK */
+ "", /* characters do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "'" /* characters end with a ' character */
+};
+
+TOKEN EiffelStringToken = {
+ "string", /* used by error messages involving this token */
+ "@S", /* Lout command for formatting strings */
+ "", /* No alternate command */
+ { "\"" }, /* strings begin with a " character */
+ NotDoubleQuote, /* inside, any printable except " is OK */
+ "%", /* within strings, % is the escape character */
+ AllPrintable, /* after escape char, any printable char is OK */
+ "", /* strings do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "\"" /* strings end with a " character */
+};
+
+TOKEN EiffelCharacterToken = {
+ "character", /* used by error messages involving this token */
+ "@C", /* Lout command for formatting characters */
+ "", /* No alternate command */
+ { "'" }, /* characters begin with a ' character */
+ NotQuote, /* inside, any printable except ' is OK */
+ "%", /* within characters, % is the escape character */
+ AllPrintable, /* after escape char, any printable char is OK */
+ "", /* characters do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "'" /* characters end with a ' character */
+};
+
+TOKEN IdentifierToken = {
+ "identifier", /* used by error messages involving this token */
+ "@D", /* Lout command for formatting identifiers */
+ "@K", /* Alternate command (for keywords) */
+ { SepLetters, "_" }, /* identifiers begin with any letter or _ */
+ Letter_Digit, /* inside, letters, underscores, digits are OK */
+ "", /* no escape character within identifiers */
+ "", /* so nothing legal after escape char either */
+ "", /* identifiers do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "" /* identifiers do not end with a delimiter */
+};
+
+TOKEN NumberToken = {
+ "number", /* used by error messages involving this token */
+ "@N", /* Lout command for formatting numbers */
+ "", /* No alternate command */
+ { SepDigits }, /* numbers must begin with a digit */
+ "0123456789.eE", /* inside, digits, decimal point, exponent */
+ "", /* no escape character within numbers */
+ "", /* so nothing legal after escape char either */
+ "", /* numbers do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "" /* numbers do not end with a delimiter */
+};
+
+TOKEN CCommentToken = {
+ "comment", /* used by error messages involving this token */
+ "@C", /* Lout command for formatting comments */
+ "", /* No alternate command */
+ { "/*" }, /* comments begin with this character pair */
+ AllPrintableTabNL, /* inside, any printable char, tab, or nl is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "", /* C comments do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "*/" /* comments end with this character pair */
+};
+
+TOKEN CCommentEscapeToken = {
+ "Lout escape", /* used by error messages involving this token */
+ NULL, /* no Lout command since passed through unformatted */
+ "", /* No alternate command */
+ { "/*@" }, /* comments begin with this character pair */
+ AllPrintableTabNL, /* inside, any printable char, tab, or nl is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "", /* no "inner escape" in escape comments */
+ "", /* so no end of "inner escape" either */
+ "*/" /* comments end with this character pair */
+};
+
+TOKEN CPPCommentToken = {
+ "comment", /* used by error messages involving this token */
+ "@C", /* Lout command for formatting comments */
+ "", /* No alternate command */
+ { "//" }, /* comments begin with this character pair */
+ AllPrintable, /* inside, any printable char is OK (not NL) */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "", /* C comments do not permit "inner escapes" */
+ "", /* and so there is no end innner escape either */
+ "" /* no end delimiter (end of line will end it) */
+};
+
+TOKEN CPPCommentEscapeToken = {
+ "Lout escape", /* used by error messages involving this token */
+ NULL, /* no Lout command since passed through unformatted */
+ "", /* No alternate command */
+ { "//@" }, /* comments begin with this character pair */
+ AllPrintable, /* inside, any printable char is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "", /* no "inner escape" in escape comments */
+ "", /* so no end of "inner escape" either */
+ "" /* no end delimiter (end of line will end it) */
+};
+
+TOKEN EiffelCommentToken = {
+ "comment", /* used by error messages involving this token */
+ "@C", /* Lout command for formatting comments */
+ "", /* No alternate command */
+ { "--" }, /* comments begin with this character pair */
+ AllPrintable, /* inside, any printable char is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "`", /* start of "inner escape" in Eiffel comment */
+ "'", /* end of "inner escape" in Eiffel comment */
+ "" /* no ending delimiter; end of line will end it */
+};
+
+TOKEN EiffelCommentEscapeToken = {
+ "Lout escape", /* used by error messages involving this token */
+ NULL, /* no Lout command since passed through unformatted */
+ "", /* No alternate command */
+ { "--@" }, /* comments begin with this character pair */
+ AllPrintable, /* inside, any printable char is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "", /* no "inner escape" in escape comments */
+ "", /* so no end of "inner escape" either */
+ "" /* no ending delimiter; end of line will end it */
+};
+
+TOKEN BlueCommentToken = {
+ "comment", /* used by error messages involving this token */
+ "@C", /* Lout command for formatting comments */
+ "", /* No alternate command */
+ { "==", "--" }, /* comments begin with this character pair */
+ AllPrintable, /* inside, any printable char is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "`", /* start of "inner escape" in Blue comment */
+ "'", /* end of "inner escape" in Blue comment */
+ "" /* no ending delimiter; end of line will end it */
+};
+
+TOKEN BlueCommentEscapeToken = {
+ "Lout escape", /* used by error messages involving this token */
+ NULL, /* no Lout command since passed through unformatted */
+ "", /* No alternate command */
+ { "==@", "--@" }, /* comments begin with this character pair */
+ AllPrintable, /* inside, any printable char is OK */
+ "", /* no escape character within comments */
+ "", /* so nothing legal after escape char either */
+ "", /* no "inner escape" in escape comments */
+ "", /* so no end of "inner escape" either */
+ "" /* no ending delimiter; end of line will end it */
+};
+
+
+#define FixedToken(str, command) /* define fixed-string token */ \
+{ \
+ str, /* name used for debugging only */ \
+ command, /* Lout command for formatting this */ \
+ "", /* No alternate command */ \
+ { str }, /* token begins (and ends!) with this */ \
+ "", /* nothing inside, since no inside */ \
+ "", /* no escape character */ \
+ "", /* so nothing after escape character */ \
+ "", /* no inner escape either */ \
+ "", /* so no end inner escape */ \
+ "" /* no ending delimiter */ \
+}
+
+TOKEN HashToken = FixedToken("#", "@O");
+TOKEN ExclamationToken = FixedToken("!", "@O");
+TOKEN PercentToken = FixedToken("%", "@O");
+TOKEN HatToken = FixedToken("^", "@O");
+TOKEN AmpersandToken = FixedToken("&", "@O");
+TOKEN StarToken = FixedToken("*", "@ST");
+TOKEN SlashToken = FixedToken("/", "@O");
+TOKEN ArrowToken = FixedToken("->", "arrowright @A @O");
+TOKEN BackSlashToken = FixedToken("\\", "@O");
+TOKEN LeftParenToken = FixedToken("(", "@O");
+TOKEN RightParenToken = FixedToken(")", "@O");
+TOKEN MinusToken = FixedToken("-", "@M");
+TOKEN PlusToken = FixedToken("+", "plus @A @O");
+TOKEN EqualToken = FixedToken("=", "equal @A @O");
+TOKEN LeftBraceToken = FixedToken("{", "@O");
+TOKEN RightBraceToken = FixedToken("}", "@O");
+TOKEN BarToken = FixedToken("|", "@O");
+TOKEN CircumToken = FixedToken("~", "@O");
+TOKEN LeftBracketToken = FixedToken("[", "@O");
+TOKEN RightBracketToken = FixedToken("]", "@O");
+TOKEN SemicolonToken = FixedToken(";", "@O");
+TOKEN ColonToken = FixedToken(":", "@O");
+TOKEN LessToken = FixedToken("<", "less @A @O");
+TOKEN GreaterToken = FixedToken(">", "greater @A @O");
+TOKEN QuestionToken = FixedToken("?", "@O");
+TOKEN CommaToken = FixedToken(",", "@O");
+TOKEN DotToken = FixedToken(".", "@O");
+TOKEN EiffelDotToken = FixedToken(".", "@ED");
+TOKEN LessEqualToken = FixedToken("<=", "lessequal @A @O");
+TOKEN GreaterEqualToken = FixedToken(">=", "greaterequal @A @O");
+TOKEN CNotEqualToken = FixedToken("!=", "notequal @A @O");
+TOKEN EiffelNotEqualToken = FixedToken("/=", "notequal @A @O");
+TOKEN BlueNotEqualToken = FixedToken("<>", "notequal @A @O");
+TOKEN AssignToken = FixedToken(":=", "@O");
+TOKEN QuestionAssignToken = FixedToken("?=", "@O");
+TOKEN DollarToken = FixedToken("$", "@O");
+TOKEN ImpliesToken = FixedToken("=>", "implies @A @O");
+
+
+/*****************************************************************************/
+/* */
+/* LANGUAGE - put your language declarations in this section. */
+/* */
+/*****************************************************************************/
+
+typedef struct lang_rec {
+ char *names[MAX_ARRAY_LENGTH];
+ char *default_style;
+ TOKEN *tokens[MAX_ARRAY_LENGTH];
+ char *keywords[MAX_ARRAY_LENGTH];
+} LANGUAGE;
+
+LANGUAGE CLanguage = {
+ { "C", "c", "C++", "c++" },
+ "fixed",
+ {
+ &CStringToken, &CCharacterToken, &IdentifierToken, &NumberToken,
+ &CCommentToken, &CCommentEscapeToken,
+ &CPPCommentToken, &CPPCommentEscapeToken,
+ &HashToken, &ExclamationToken, &PercentToken, &HatToken,
+ &AmpersandToken, &StarToken, &LeftParenToken, &RightParenToken,
+ &MinusToken, &PlusToken, &EqualToken, &LeftBraceToken, &RightBraceToken,
+ &BarToken, &CircumToken, &LeftBracketToken, &RightBracketToken,
+ &SemicolonToken, &ColonToken, &LessToken, &GreaterToken,
+ &QuestionToken, &CommaToken, &DotToken, &SlashToken, &BackSlashToken,
+ &ArrowToken, &LessEqualToken, &GreaterEqualToken, &CNotEqualToken
+ },
+
+ { "asm", "auto", "break", "case", "catch", "char", "class", "const",
+ "continue", "default", "delete", "do", "double", "else", "enum",
+ "extern", "float", "for", "friend", "goto", "if", "inline", "int",
+ "long", "new", "operator", "private", "protected", "public", "register",
+ "return", "short", "signed", "sizeof", "static", "struct", "switch",
+ "template", "this", "throw", "try", "typedef", "union", "unsigned",
+ "virtual", "void", "volatile", "while",
+ }
+};
+
+
+LANGUAGE EiffelLanguage = {
+ { "Eiffel", "eiffel" },
+ "varying",
+ {
+ &EiffelStringToken, &EiffelCharacterToken, &IdentifierToken, &NumberToken,
+ &EiffelCommentToken, &EiffelCommentEscapeToken,
+ &SemicolonToken, &CommaToken, &ColonToken, &EiffelDotToken,
+ &ExclamationToken, &EqualToken, &EiffelNotEqualToken, &LeftParenToken,
+ &RightParenToken, &LeftBracketToken, &RightBracketToken, &LeftBraceToken,
+ &RightBraceToken, &AssignToken, &QuestionAssignToken, &PlusToken,
+ &MinusToken, &DollarToken, &HatToken, &SlashToken, &BackSlashToken,
+ &LessToken, &GreaterToken, &LessEqualToken, &GreaterEqualToken
+ },
+
+ { "alias", "all", "and", "as", "check", "class", "creation", "debug",
+ "deferred", "do", "else", "elseif", "end", "ensure", "expanded",
+ "export", "external", "false", "feature", "from", "frozen", "if",
+ "implies", "indexing", "infix", "inherit", "inspect", "invariant",
+ "is", "like", "local", "loop", "obsolete", "old", "once", "or",
+ "prefix", "redefine", "rename", "require", "rescue", "retry", "select",
+ "separate", "strip", "then", "true", "undefine", "unique", "until",
+ "variant", "when", "xor", "not", "interface"
+ }
+};
+
+
+LANGUAGE BlueLanguage = {
+ { "Blue", "blue" },
+ "varying",
+ {
+ &CStringToken, &IdentifierToken, &NumberToken,
+ &BlueCommentToken, &BlueCommentEscapeToken,
+ &CommaToken, &LessToken, &GreaterToken, &ColonToken, &AssignToken,
+ &LeftParenToken, &RightParenToken, &LeftBracketToken, &RightBracketToken,
+ &QuestionAssignToken, &ExclamationToken, &EiffelDotToken, &ImpliesToken,
+ &EqualToken, &BlueNotEqualToken, &LeftBraceToken, &RightBraceToken,
+ &PlusToken, &MinusToken, &StarToken, &SlashToken, &HatToken,
+ &LessEqualToken, &GreaterEqualToken
+
+ },
+
+ { "and", "assert", "builtin", "case", "class", "const", "create",
+ "creation", "deferred", "div", "do", "else", "elseif", "end",
+ "Enumeration", "enumeration", "exit", "if", "in", "interface",
+ "internal", "invariant", "is", "loop", "manifest", "mod", "not",
+ "of", "old", "on", "or", "post", "pre", "redefined", "return",
+ "routines", "super", "then", "uses", "var"
+ }
+};
+
+
+LANGUAGE *languages[] = {
+ & CLanguage,
+ & EiffelLanguage,
+ & BlueLanguage,
+};
+
+/*****************************************************************************/
+/* */
+/* If you are adding a new language, you don't need to change anything */
+/* below this point. Just repeating: don't change anything below here. */
+/* */
+/*****************************************************************************/
+
+
+/*****************************************************************************/
+/* */
+/* Global constants and variables */
+/* */
+/*****************************************************************************/
+#define DEBUG_SETUP 0
+#define DEBUG_PROCESS 0
+#define DEBUG_TRIE 0
+#define DEBUG_NEXTCHAR 0
+#define DEBUG_PREFIXEQ 0
+#define DEBUG_EMIT 0
+#define DEBUG_MAIN 0
+
+#define PROG2LOUT_VERSION "prg2lout Version 1.0 (February 2000)"
+#define BOOLEAN unsigned
+#define FALSE 0
+#define TRUE 1
+#define MAX_LINE 1024
+
+/* print styles */
+#define NO_STYLE 0
+#define FIXED_STYLE 1
+#define VARYING_STYLE 2
+#define SYMBOL_STYLE 3
+
+static char file_name[MAX_LINE]; /* current input file name */
+static char curr_line[MAX_LINE]; /* current input line */
+static int line_num; /* current input line number */
+static int line_pos; /* current input column number */
+static BOOLEAN raw_seen; /* TRUE if -r (raw mode) */
+
+static BOOLEAN headers_option; /* TRUE if no -n option (headers) */
+static int style_option; /* value of -p option, or NO_STYLE */
+static char *font_option; /* value of -f option, else null */
+static char *size_option; /* value of -s option, else null */
+static char *line_option; /* value of -v option, else null */
+static char *tabin_option; /* value of -t option, else null */
+static char *tabout_option; /* value of -T option, else null */
+static char *language_option; /* value of -l option, else null */
+
+static BOOLEAN tab_by_spacing; /* TRUE if using space chars to tab */
+static int tab_in; /* tab interval, value of -t option */
+static float tab_out; /* tab interval width (-T option) */
+static char tab_unit; /* unit of measurement for tab */
+
+char *ErrorHeader()
+{ static char buff[MAX_LINE];
+ if( line_num == 0 )
+ sprintf(buff, "prg2lout");
+ else if( raw_seen )
+ sprintf(buff, "prg2lout %d,%d", line_num, line_pos);
+ else
+ sprintf(buff, "prg2lout %s %d,%d", file_name, line_num, line_pos);
+ return buff;
+}
+
+#define GetArg(arg, message, null_ok) \
+{ if( strcmp(argv[arg_pos]+2, "") != 0 ) \
+ arg = argv[arg_pos]+2; \
+ else if( !null_ok && arg_pos < argc-1 && *argv[arg_pos+1] != '-' ) \
+ arg = argv[++arg_pos]; \
+ else if( null_ok ) \
+ arg = (char *) NULL; \
+ else \
+ { fprintf(err_fp, "%s: %s\n", ErrorHeader(), message); \
+ exit(1); \
+ } \
+} /* end GetArg */
+
+
+/*****************************************************************************/
+/* */
+/* char *EchoToken(TOKEN *t) */
+/* */
+/* Print a brief resume of token t */
+/* */
+/*****************************************************************************/
+
+char *EchoToken(TOKEN *t)
+{ static char buff[MAX_LINE];
+ if( t == (TOKEN *) NULL )
+ sprintf(buff, "(NULL)");
+ else
+ sprintf(buff, "%s", t->name);
+ return buff;
+}
+
+
+
+/*****************************************************************************/
+/* */
+/* TRIE */
+/* */
+/* We use a trie to match the input against the opening pattern of each */
+/* token, since some tokens (e.g. <=, // etc.) have multi-character */
+/* opening patterns. */
+/* */
+/*****************************************************************************/
+
+typedef struct trie_node {
+ struct trie_node *sub[MAX_CHAR];
+ TOKEN *value[MAX_CHAR];
+} *TRIE;
+
+/*****************************************************************************/
+/* */
+/* BOOLEAN TrieInsert(&T, str, val) */
+/* */
+/* Insert str into trie T. May need a new root so pass T by reference. */
+/* Return FALSE if the insertion failed, either because the string was */
+/* empty, or because it was the same as a previously inserted string. */
+/* */
+/*****************************************************************************/
+
+BOOLEAN TrieInsert(TRIE *T, char *str, TOKEN *val)
+{ BOOLEAN res;
+ if( DEBUG_TRIE )
+ fprintf(stderr, "[ TrieInsert(T, %s, %s)\n", str, EchoToken(val));
+ if( *str == '\0' )
+ res = FALSE;
+ else
+ {
+ if( *T == (TRIE) NULL )
+ *T = (TRIE) calloc(1, sizeof(struct trie_node)); /* will set all to 0 */
+ if( *(str + 1) != '\0' )
+ res = TrieInsert(&((*T)->sub[(int) *str]), str + 1, val);
+ else if( (*T)->value[(int) *str] != (TOKEN *) NULL )
+ res = FALSE;
+ else
+ {
+ (*T)->value[(int) *str] = val;
+ res = TRUE;
+ }
+ }
+ if( DEBUG_TRIE )
+ fprintf(stderr, "] TrieInsert(T, %s, %s) returning %s\n", str,
+ EchoToken(val), res ? "TRUE" : "FALSE");
+ return res;
+}
+
+/*****************************************************************************/
+/* */
+/* TOKEN *TrieRetrieve(T, str, &len) */
+/* */
+/* Find the longest prefix of string str in T. If this is empty, return */
+/* NULL. If non-empty, return the corresponding value as the result, and */
+/* the length of the prefix in *len. */
+/* */
+/*****************************************************************************/
+
+TOKEN *TrieRetrieve(TRIE T, char *str, int *len)
+{ TOKEN *res; int i;
+ if( DEBUG_TRIE )
+ fprintf(stderr, "[ TrieRetrieve(T, %s, len)\n", str);
+ res = (TOKEN *) NULL;
+ *len = 0;
+ for( i = 0; T != (TRIE) NULL; T = T->sub[(int) str[i]], i++ )
+ {
+ if( DEBUG_TRIE )
+ fprintf(stderr, " i = %d, res = %s\n", i, EchoToken(res));
+ if( T->value[(int) str[i]] != (TOKEN *) NULL )
+ {
+ res = T->value[(int) str[i]];
+ *len = i+1;
+ }
+ }
+ if( DEBUG_TRIE )
+ fprintf(stderr, "] TrieRetrieve returning (*len = %d) %s\n",
+ *len, EchoToken(res));
+ return res;
+}
+
+
+/*****************************************************************************/
+/* */
+/* HASH_TABLE */
+/* */
+/* We use a hash table to hold the keywords. There is no associated */
+/* value, we just want to know whether they are there or not. */
+/* */
+/* NB MAX_SYM must be somewhat larger than the number of keywords. */
+/* */
+/*****************************************************************************/
+#define MAX_SYM 309
+
+static char *HashTable[MAX_SYM]; /* will initialze to NULL */
+static int HashTableCount = 0; /* number of entries */
+
+static int hash(char *key)
+{ int i, res;
+ res = 0;
+ for( i = 0; key[i] != '\0'; i++ )
+ { res += key[i];
+ }
+ return res % MAX_SYM;
+} /* end hash */
+
+void HashTableInsert(char *str, FILE *err_fp)
+{ int i;
+ if( DEBUG_SETUP )
+ fprintf(stderr, "[ HashTableInsert(%s)\n", str);
+ if( HashTableCount >= MAX_SYM - 20 )
+ {
+ fprintf(err_fp, "%s internal error: full hash table (increase MAX_SYM)\n",
+ ErrorHeader());
+ abort();
+ }
+ for( i = hash(str); HashTable[i] != (char *) NULL; i = (i+1) % MAX_SYM );
+ HashTable[i] = str;
+ HashTableCount++;
+ if( DEBUG_SETUP )
+ fprintf(stderr, "] HashTableInsert(%s)\n", str);
+}
+
+BOOLEAN HashTableRetrieve(char *str)
+{ int i;
+ for( i = hash(str); HashTable[i] != (char *) NULL; i = (i+1) % MAX_SYM )
+ if( strcmp(HashTable[i], str) == 0 )
+ return TRUE;
+ return FALSE;
+}
+
+
+/*****************************************************************************/
+/* */
+/* BACK END */
+/* */
+/* This is the code that actually prints the output file. It accumulates */
+/* each token and only prints it at the end of the token, so that it can */
+/* check whether an alternative command ought to be printed (keywords). */
+/* */
+/*****************************************************************************/
+
+static char save_token[MAX_LINE]; /* the token text */
+static int save_len; /* index of \0 in save_token */
+static BOOLEAN save_on = FALSE; /* TRUE when saving */
+
+/*****************************************************************************/
+/* */
+/* EmitRaw(ch, out_fp, FILE *err_fp) */
+/* */
+/* Emit this character immediately. This is only legal when not saving. */
+/* All characters printed on the output file should pass through here, */
+/* since EmitRaw keeps track of where we are on the output line, in order */
+/* to handle tab characters correctly. */
+/* */
+/* NB out_linepos is the column where the *next* character will go, and */
+/* it counts the first column on the line as column zero. It understands */
+/* that a tab character always produces at least one space, and that the */
+/* character after a tab goes in a column whose number mod tab_in is zero. */
+/* */
+/*****************************************************************************/
+
+void EmitRaw(char ch, FILE *out_fp, FILE *err_fp)
+{
+ static int out_linepos = 0; /* output line position */
+ static BOOLEAN out_linestart = TRUE; /* TRUE if out line start */
+
+ if( DEBUG_EMIT )
+ fprintf(stderr, "EmitRaw(%c); out_linepos %d, out_linestart %s\n",
+ ch, out_linepos, out_linestart ? "TRUE" : "FALSE");
+ if( save_on )
+ {
+ fprintf(err_fp, "%s internal error (EmitRaw save_on)\n", ErrorHeader());
+ abort();
+ }
+ if( ch == '\t' )
+ {
+ if( tab_by_spacing )
+ { putc(' ', out_fp);
+ out_linepos++;
+ while( out_linepos % tab_in != 0 )
+ { putc(' ', out_fp);
+ out_linepos++;
+ }
+ }
+ else
+ {
+ out_linepos++;
+ while( out_linepos % tab_in != 0 )
+ { out_linepos++;
+ }
+ if( out_linestart )
+ { fprintf(out_fp, "$>%.1f%c {}", tab_out, tab_unit);
+ /* NB {} is required in case nothing follows on this line */
+ }
+ else
+ { fprintf(out_fp, "$>%.1f%ct {}", (out_linepos/tab_in)*tab_out, tab_unit);
+ }
+ }
+ }
+ else if( ch == '\n' )
+ {
+ fputc(ch, out_fp);
+ out_linepos = 0;
+ out_linestart = TRUE;
+ }
+ else
+ {
+ fputc(ch, out_fp);
+ out_linepos++;
+ if( ch != ' ' )
+ out_linestart = FALSE;
+ }
+ if( DEBUG_EMIT )
+ fprintf(stderr, "EmitRaw(%c) returning; out_linepos %d, out_linestart %s\n",
+ ch, out_linepos, out_linestart ? "TRUE" : "FALSE");
+} /* end EmitRaw */
+
+
+/*****************************************************************************/
+/* */
+/* StartEmit(FILE *err_fp) */
+/* */
+/* Start the emission of a token. */
+/* */
+/*****************************************************************************/
+
+void StartEmit(FILE *err_fp)
+{
+ if( save_on )
+ {
+ fprintf(err_fp, "%s internal error (StartEmit)\n", ErrorHeader());
+ abort();
+ }
+ save_on = TRUE;
+ save_len = 0;
+}
+
+
+/*****************************************************************************/
+/* */
+/* EndEmit(LANGUAGE *lang, TOKEN *current_token, FILE *out_fp, *err_fp) */
+/* */
+/* End emitting a token of this type on file out_fp. */
+/* */
+/*****************************************************************************/
+
+void EndEmit(LANGUAGE *lang, TOKEN *current_token, FILE *out_fp, FILE *err_fp)
+{ BOOLEAN altern; int i;
+ if( !save_on )
+ {
+ fprintf(err_fp, "%s internal error (EndEmit)\n", ErrorHeader());
+ abort();
+ }
+ save_on = FALSE;
+
+ if( save_len == 0 )
+ {
+ /* nothing in token, must be resumed after a newline or tab */
+ /* so emit nothing in this case */
+ }
+ else if( current_token->command == (char *) NULL )
+ {
+ /* no command, which means to emit this token without its delimiters */
+ for( i = 0; i < save_len; i++ )
+ EmitRaw(save_token[i], out_fp, err_fp);
+ }
+ else
+ {
+ /* if there is an alternate command in this language, check whether the */
+ /* completed token is a keyword, and if so apply the alternate command. */
+ altern = (lang != (LANGUAGE *) NULL
+ && current_token->alternate_command[0] != '\0'
+ && HashTableRetrieve(save_token));
+
+ putc('{', out_fp);
+ fputs(altern ? current_token->alternate_command : current_token->command,
+ out_fp);
+ putc('"', out_fp);
+ for( i = 0; i < save_len; i++ )
+ {
+ if( save_token[i] == '"' || save_token[i] == '\\' )
+ putc('\\', out_fp);
+ EmitRaw(save_token[i], out_fp, err_fp);
+ }
+ putc('"', out_fp);
+ putc('}', out_fp);
+ }
+} /* end EndEmit */
+
+
+/*****************************************************************************/
+/* */
+/* Emit(char ch, TOKEN *current_token, FILE *out_fp, FILE *err_fp) */
+/* */
+/* Emit one character. */
+/* */
+/*****************************************************************************/
+
+void Emit(char ch, TOKEN *current_token, FILE *out_fp, FILE *err_fp)
+{
+ if( !save_on )
+ {
+ fprintf(err_fp, "%s internal error (EmitChar)\n", ErrorHeader());
+ abort();
+ }
+ if( ch == '\n' || ch == '\t' )
+ {
+ EndEmit((LANGUAGE *) NULL, current_token, out_fp, err_fp);
+ EmitRaw(ch, out_fp, err_fp);
+ StartEmit(err_fp);
+ }
+ else
+ {
+ save_token[save_len++] = ch;
+ save_token[save_len] = '\0';
+ }
+} /* end Emit */
+
+
+/*****************************************************************************/
+/* */
+/* SetupTokens(LANGUAGE *lang) */
+/* */
+/* Set up the runtime token structures. This involves initializing the */
+/* chtype and escape_chtype fields for each token type in the chosen */
+/* language, and loading the trie with all the opening delimiters of all */
+/* the tokens. */
+/* */
+/*****************************************************************************/
+#define LEGAL 1
+#define ESCAPE 2
+#define INNER_ESCAPE 3
+
+TRIE Trie = (TRIE) NULL;
+
+void SetupTokens(LANGUAGE *lang, FILE *err_fp)
+{ TOKEN *t; int i, j;
+ if( DEBUG_SETUP )
+ fprintf(stderr, "SetupTokens(%s)\n", lang->names[0]);
+
+ /* set up each token in the language */
+ for( i = 0; lang->tokens[i] != (TOKEN *) NULL; i++ )
+ {
+ t = lang->tokens[i];
+ if( DEBUG_SETUP )
+ fprintf(stderr, "SetupTokens token starting %s\n", t->starts[0]);
+
+ /* set up the chtype table for this token */
+ for( j = 0; t->legal[j] != '\0'; j++ )
+ t->chtype[(int) t->legal[j]] = LEGAL;
+ if( t->escape[0] != '\0' )
+ t->chtype[(int) t->escape[0]] = ESCAPE;
+ if( t->inner_escape[0] != '\0' )
+ t->chtype[(int) t->inner_escape[0]] = INNER_ESCAPE;
+
+ /* set up the escape_chtype table for this token */
+ for( j = 0; t->escape_legal[j] != '\0'; j++ )
+ t->escape_chtype[(int) t->escape_legal[j]] = LEGAL;
+
+ /* load the opening delimiters of this token into the trie */
+ for( j = 0; t->starts[j] != (char *) NULL; j++ )
+ {
+ if( !TrieInsert(&Trie, t->starts[j], t) )
+ {
+ if( *(t->starts[j]) == '\0' )
+ fprintf(err_fp, "%s: empty starting delimiter\n", ErrorHeader());
+ else
+ fprintf(err_fp, "%s: starting delimiter %s appears twice\n",
+ ErrorHeader(), t->starts[j]);
+ }
+ }
+
+ if( DEBUG_SETUP )
+ fprintf(stderr, "SetupTokens token ending %s\n", t->starts[0]);
+ }
+
+ /* load the keyword hash table */
+ for( j = 0; lang->keywords[j] != (char *) NULL; j++ )
+ HashTableInsert(lang->keywords[j], err_fp);
+
+ if( DEBUG_SETUP )
+ fprintf(stderr, "SetupTokens(%s) returning.\n", lang->names[0]);
+} /* end SetupTokens */
+
+
+/*****************************************************************************/
+/* */
+/* BOOLEAN PrefixEq(char *str, char *prefix) */
+/* */
+/* Returns TRUE if str (unterminated) begins with prefix (terminated). */
+/* */
+/*****************************************************************************/
+
+BOOLEAN PrefixEq(char *str, char *prefix)
+{ char *p, *q;
+ for( p = str, q = prefix; *q != '\0' && *p == *q; p++, q++ );
+ if( DEBUG_PREFIXEQ )
+ fprintf(stderr, "PrefixEq(%s, %s) returning %s\n",
+ str, prefix, *q == '\0' ? "TRUE" : "FALSE");
+ return (*q == '\0');
+} /* end PrefixEq */
+
+
+/*****************************************************************************/
+/* */
+/* NextChar(FILE *in_fp) */
+/* */
+/* Move to next character in the input file. This may involve changing */
+/* global variables curr_line, line_num, and line_pos; the new character */
+/* may be found in curr_line[line_pos]. */
+/* */
+/* NextChar does not skip any characters at all. When end of file is */
+/* reached, curr_line[line_pos] contains '\0'. */
+/* */
+/*****************************************************************************/
+
+void NextChar(FILE *in_fp)
+{
+ if( curr_line[line_pos] == '\n' )
+ {
+ /* need a new line */
+ line_num++;
+ line_pos = 1;
+ if( fgets(&curr_line[1], MAX_LINE+2, in_fp) == (char *) NULL )
+ curr_line[1] = '\0';
+ }
+ else line_pos++; /* will yield '\0' as desired if EOF before end of line */
+ if( DEBUG_NEXTCHAR )
+ fprintf(stderr, "after NextChar, line_num %d, line_pos %d, curr_line %s",
+ line_num, line_pos, &curr_line[1]);
+} /* end NextChar */
+
+
+/*****************************************************************************/
+/* */
+/* BOOLEAN Printable(char ch) */
+/* */
+/* TRUE if ch is a printable character. Used only by error messages so */
+/* can be slow. */
+/* */
+/*****************************************************************************/
+
+BOOLEAN Printable(char ch)
+{ char *p;
+ for( p = AllPrintable; *p != '\0' && *p != ch; p++ );
+ return (*p == ch);
+} /* end Printable */
+
+
+/*****************************************************************************/
+/* */
+/* Process(LANGUAGE*lang, FILE*in_fp, *out_fp, *err_fp, TOKEN *outer_token) */
+/* */
+/* Process a sequence of input tokens. If we are currently recursing */
+/* inside some other token, outer_token is non-null and is that token, */
+/* and we stop when we reach the ending delimiter of the inner escape */
+/* of that token. Otherwise, we stop at end of file. */
+/* */
+/*****************************************************************************/
+#define START 1
+#define IN_TOKEN 2
+#define IN_TOKEN_AFTER_ESCAPE 3
+#define IN_TOKEN_AFTER_INNER_ESCAPE 4
+#define STOP 5
+
+void Process(LANGUAGE *lang, FILE *in_fp, FILE *out_fp, FILE *err_fp,
+TOKEN *outer_token)
+{ TOKEN *current_token; int len, i, state;
+ if( DEBUG_PROCESS )
+ fprintf(stderr, "[ Process(%s, -, -, -, -)\n", lang->names[0]);
+
+ state = START;
+ while( curr_line[line_pos] != '\0' && state != STOP )
+ {
+ if( DEBUG_PROCESS )
+ fprintf(stderr, " state %d, ch %c\n", state, curr_line[line_pos]);
+ switch( state )
+ {
+ case START: /* between tokens */
+
+ /* check whether outer_token's delimiter is current */
+ if( outer_token != (TOKEN *) NULL &&
+ outer_token->end_inner_escape[0] != '\0' &&
+ PrefixEq(&curr_line[line_pos], outer_token->end_inner_escape) )
+ {
+ len = strlen(outer_token->end_inner_escape);
+ for( i = 0; i < len; i++ )
+ NextChar(in_fp);
+ state = STOP;
+ }
+ else switch( curr_line[line_pos] )
+ {
+
+ case ' ':
+
+ EmitRaw(' ', out_fp, err_fp);
+ NextChar(in_fp);
+ break;
+
+
+ case '\t':
+
+ if( outer_token != (TOKEN *) NULL )
+ {
+ fprintf(err_fp, "%s: replacing tab character in escape by space\n",
+ ErrorHeader());
+ EmitRaw(' ', out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ else
+ {
+ EmitRaw('\t', out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ break;
+
+
+ case '\n':
+
+ if( outer_token != (TOKEN *) NULL )
+ {
+ fprintf(err_fp, "%s: inserting %s to fix unterminated escape\n",
+ ErrorHeader(), outer_token->end_inner_escape);
+ state = STOP;
+ }
+ else
+ {
+ EmitRaw('\n', out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ break;
+
+
+ case '\f':
+
+ if( outer_token != (TOKEN *) NULL )
+ {
+ fprintf(err_fp, "%s: replacing formfeed character in escape by space\n",
+ ErrorHeader());
+ EmitRaw(' ', out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ else
+ {
+ fprintf(out_fp, "\n@NP");
+ EmitRaw('\n', out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ break;
+
+
+ default:
+
+ /* look up the trie to see which token we are starting */
+ current_token = TrieRetrieve(Trie, &curr_line[line_pos], &len);
+ if( current_token == (TOKEN *) NULL )
+ {
+ /* error; no token starts here, so print error message and skip */
+ if( Printable(curr_line[line_pos]) )
+ fprintf(err_fp, "%s: skipping unexpected %c character\n",
+ ErrorHeader(), curr_line[line_pos]);
+ else
+ fprintf(err_fp,
+ "%s: skipping unexpected unprintable character (octal %o)\n",
+ ErrorHeader(), (int) curr_line[line_pos]);
+ NextChar(in_fp);
+ }
+ else
+ {
+ /* have a token, so start emitting it */
+ if( DEBUG_PROCESS )
+ {
+ fprintf(stderr, "current_token (len = %d): %s\n",
+ len, EchoToken(current_token));
+ }
+ StartEmit(err_fp);
+ for( i = 0; i < len; i++ )
+ {
+ /* ***
+ if( DEBUG_PROCESS )
+ fprintf(stderr, " emitting delim (i %d, len %d)\n", i, len);
+ *** */
+ if( current_token->command != (char *) NULL )
+ Emit(curr_line[line_pos], current_token, out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ state = IN_TOKEN;
+ }
+ }
+ break;
+
+
+ case IN_TOKEN: /* within a token; current_token says which kind */
+
+ /* check for ending delimiter if there is one */
+ if( current_token->end_delimiter[0] != '\0' &&
+ PrefixEq(&curr_line[line_pos], current_token->end_delimiter) )
+ {
+ if( DEBUG_PROCESS )
+ fprintf(stderr, " PrefixEq(-, %s) so finishing token\n",
+ current_token->end_delimiter);
+ len = strlen(current_token->end_delimiter);
+ for( i = 0; i < len; i++ )
+ { if( current_token->command != (char *) NULL )
+ Emit(curr_line[line_pos], current_token, out_fp, err_fp);
+ NextChar(in_fp);
+ }
+ EndEmit(lang, current_token, out_fp, err_fp);
+ state = START;
+ }
+ else switch( current_token->chtype[(int) curr_line[line_pos]] )
+ {
+
+ case LEGAL:
+
+ Emit(curr_line[line_pos], current_token, out_fp, err_fp);
+ NextChar(in_fp);
+ break;
+
+
+ case ESCAPE:
+
+ NextChar(in_fp);
+ state = IN_TOKEN_AFTER_ESCAPE;
+ break;
+
+
+ case INNER_ESCAPE:
+
+ EndEmit(lang, current_token, out_fp, err_fp);
+ NextChar(in_fp);
+ Process(lang, in_fp, out_fp, err_fp, current_token);
+ state = IN_TOKEN_AFTER_INNER_ESCAPE;
+ break;
+
+
+ default:
+
+ if( current_token->end_delimiter[0] != '\0' )
+ {
+ /* error: token ends at delimiter, not at unexpected character */
+ if( Printable(curr_line[line_pos]) )
+ fprintf(err_fp, "%s: skipping unexpected %c character in %s\n",
+ ErrorHeader(), curr_line[line_pos], current_token->name);
+ else
+ fprintf(err_fp,
+ "%s: skipping unexpected unprintable character (octal %o) in %s\n",
+ ErrorHeader(), (int) curr_line[line_pos], current_token->name);
+ NextChar(in_fp);
+ }
+ else
+ {
+ /* normal termination after last legal character */
+ EndEmit(lang, current_token, out_fp, err_fp);
+ state = START;
+ }
+ break;
+
+
+ }
+ break;
+
+
+ case IN_TOKEN_AFTER_ESCAPE:
+
+ if( current_token->escape_chtype[(int) curr_line[line_pos]] == LEGAL )
+ {
+ Emit(current_token->escape[0], current_token, out_fp, err_fp);
+ Emit(curr_line[line_pos], current_token, out_fp, err_fp);
+ }
+ else
+ {
+ if( Printable(curr_line[line_pos]) )
+ fprintf(err_fp, "%s: skipping %c%c in %s, since %c not legal here\n",
+ ErrorHeader(), current_token->escape[0], curr_line[line_pos],
+ current_token->name, curr_line[line_pos]);
+ else
+ fprintf(err_fp,
+ "%s: skipping %c and unprintable unexpected character (octal %o)\n",
+ ErrorHeader(), current_token->escape[0], (int) curr_line[line_pos]);
+ }
+ NextChar(in_fp);
+ state = IN_TOKEN;
+ break;
+
+
+ case IN_TOKEN_AFTER_INNER_ESCAPE:
+
+ /* ending delimiter of inner escape has been read over */
+ StartEmit(err_fp);
+ state = IN_TOKEN;
+ break;
+
+
+ default:
+
+ fprintf(err_fp, "%s internal error (state = %d)\n",
+ ErrorHeader(), state);
+ abort();
+ break;
+ }
+ }
+
+ /* at end, need to tidy up any residual messiness */
+ switch( state )
+ {
+
+ case START:
+ case STOP:
+
+ /* we stopped outside any token, or after an escape */
+ break;
+
+
+ case IN_TOKEN:
+
+ /* we stopped in a token (only a problem if it ends with a delimiter) */
+ if( current_token->end_delimiter[0] != '\0' )
+ {
+ if( outer_token == (TOKEN *) NULL )
+ fprintf(err_fp, "%s: program text ended within %s\n",
+ ErrorHeader(), current_token->name);
+ else
+ fprintf(err_fp, "%s: %s escape ended within %s\n",
+ ErrorHeader(), outer_token->name, current_token->name);
+ EndEmit(lang, current_token, out_fp, err_fp);
+ }
+ break;
+
+
+ case IN_TOKEN_AFTER_ESCAPE:
+
+ /* we stopped after the escape character */
+ fprintf(err_fp, "%s: skipping %c at end of program text\n",
+ ErrorHeader(), current_token->escape[0]);
+ EndEmit(lang, current_token, out_fp, err_fp);
+ break;
+
+
+ case IN_TOKEN_AFTER_INNER_ESCAPE:
+
+ /* we stopped after an inner escape (NB no EndEmit in this case) */
+ if( current_token->end_delimiter[0] != '\0' )
+ {
+ if( outer_token == (TOKEN *) NULL )
+ fprintf(err_fp, "%s: program text ended within %s after escape\n",
+ ErrorHeader(), current_token->name);
+ else
+ fprintf(err_fp, "%s: %s escape ended within %s after escape\n",
+ ErrorHeader(), outer_token->name, current_token->name);
+ }
+ break;
+
+
+ default:
+
+ fprintf(err_fp, "%s: internal error (state %d)\n",
+ ErrorHeader(), state);
+ abort();
+ break;
+
+ }
+} /* end Process */
+
+
+/*****************************************************************************/
+/* */
+/* PrintUsage(fp) */
+/* */
+/* Print usage message on file fp. */
+/* */
+/*****************************************************************************/
+
+void PrintUsage(FILE *fp)
+{ int i;
+ fprintf(fp, "\n");
+ fprintf(fp, "usage: prg2lout <options> <files>\n\n");
+ fprintf(fp, " where <options> can be\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " -r raw mode (used within Lout only)\n");
+ fprintf(fp, " -i<file> take input from <file>\n");
+ fprintf(fp, " -o<file> send output to <file>\n");
+ fprintf(fp, " -e<file> send error messages to <file>\n");
+ fprintf(fp, " -l<language> input is in this programming language\n");
+ fprintf(fp, " -p<style> print style: fixed, varying, symbol\n");
+ fprintf(fp, " -f<family> font family (e.g. Times)\n");
+ fprintf(fp, " -s<size> font size (e.g. 10p or 12p)\n");
+ fprintf(fp, " -v<space> line spacing (e.g. 1.1fx)\n");
+ fprintf(fp, " -t<num> tab interval (e.g. 8 is default)\n");
+ fprintf(fp, " -T<dist> output tab interval (e.g. 0.5i)\n");
+ fprintf(fp, " -n no file names as page headers\n");
+ fprintf(fp, " -V print version information and exit\n");
+ fprintf(fp, " -u print this usage message and exit\n");
+ fprintf(fp, "\n");
+ fprintf(fp, " and <language> (which is compulsory) can be any one of:\n");
+ for( i = 0; languages[i] != (LANGUAGE *) NULL; i++ )
+ fprintf(fp, " %s\n", languages[i]->names[0]);
+ fprintf(fp, "\n");
+} /* end PrintUsage */
+
+
+/*****************************************************************************/
+/* */
+/* main(argc, argv) */
+/* */
+/* Read command line and either process each file in turn, or, in the */
+/* raw case, do the actual conversion of one file. */
+/* */
+/*****************************************************************************/
+
+int main(int argc, char *argv[])
+{ FILE *in_fp, *out_fp, *err_fp;
+ BOOLEAN stdin_seen; int i, j, arg_pos;
+ char *infilename, *outfilename, *errfilename, *str;
+ LANGUAGE *language = (LANGUAGE *) NULL;
+ char *file_names[1024]; int file_count = 0;
+
+ /* echo command line */
+ if( DEBUG_MAIN )
+ {
+ for( i = 0; i < argc; i++ )
+ fprintf(stderr, i == 0 ? "%s" : " %s", argv[i]);
+ fprintf(stderr, "\n\n");
+ }
+
+ /* read command line */
+ in_fp = out_fp = (FILE *) NULL;
+ err_fp = stderr;
+ line_num = 0;
+ stdin_seen = raw_seen = FALSE;
+ tab_by_spacing = TRUE;
+ style_option = NO_STYLE;
+ tab_in = 8;
+ tab_out = 3;
+ tab_unit = 'f';
+ headers_option = TRUE;
+ font_option = size_option = line_option = tabin_option =
+ tabout_option = language_option = (char *) NULL;
+ if( argc == 1 )
+ { PrintUsage(err_fp);
+ exit(1);
+ }
+ for( arg_pos = 1; arg_pos < argc; arg_pos++ )
+ {
+ if( DEBUG_SETUP )
+ fprintf(stderr, "examining argument %d = \"%s\"\n",
+ arg_pos, argv[arg_pos]);
+ if( *argv[arg_pos] == '-' ) switch( *(argv[arg_pos]+1) )
+ {
+ case 'r':
+
+ if( arg_pos > 1 )
+ { fprintf(err_fp, "%s: -r must be first if it occurs at all\n",
+ ErrorHeader());
+ exit(1);
+ }
+ raw_seen = TRUE;
+ break;
+
+
+ case 'i':
+
+ /* read name of input file */
+ if( !raw_seen )
+ { fprintf(err_fp, "%s: -i illegal with -r\n", ErrorHeader());
+ exit(1);
+ }
+ if( in_fp != NULL )
+ { fprintf(err_fp, "%s: -i seen twice\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(infilename, "usage: -i<filename>", FALSE);
+
+ /* open the file */
+ in_fp = fopen(infilename, "r");
+ if( in_fp == NULL )
+ { fprintf(err_fp, "%s: cannot open input file %s\n",
+ ErrorHeader(), infilename);
+ exit(1);
+ }
+
+ /* initialize file position */
+ strcpy(file_name, infilename);
+ line_num = 1;
+ line_pos = 0;
+ break;
+
+
+ case 'o':
+
+ /* read name of output file */
+ if( out_fp != NULL )
+ { fprintf(err_fp, "%s: -o seen twice\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(outfilename, "usage: -o<filename>", FALSE);
+ out_fp = fopen(outfilename, "w");
+ if( out_fp == NULL )
+ { fprintf(err_fp, "%s: cannot open output file %s\n",
+ ErrorHeader(), outfilename);
+ exit(1);
+ }
+ break;
+
+
+ case 'e':
+
+ /* read name of error file */
+ GetArg(errfilename, "usage: -e<filename>", FALSE);
+ err_fp = fopen(errfilename, "w");
+ if( err_fp == NULL )
+ { fprintf(stderr, "%s: cannot open error file %s",
+ ErrorHeader(), errfilename);
+ exit(1);
+ }
+ break;
+
+
+ case 'p':
+
+ /* read print style */
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -p illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(str, "usage: -p<printstyle>", FALSE);
+ /* *** -l can set style, so don't do this now
+ if( style_option != NO_STYLE )
+ { fprintf(err_fp, "%s: -p option appears twice!\n", ErrorHeader());
+ exit(1);
+ }
+ else
+ *** */
+ if( strcmp(str, "fixed") == 0 )
+ { style_option = FIXED_STYLE;
+ }
+ else if( strcmp(str, "varying") == 0 )
+ { style_option = VARYING_STYLE;
+ tab_by_spacing = FALSE;
+ }
+ else if( strcmp(str, "symbol") == 0 )
+ { style_option = SYMBOL_STYLE;
+ tab_by_spacing = FALSE;
+ }
+ else
+ { fprintf(err_fp, "%s: unknown -p option %s\n", ErrorHeader(), str);
+ exit(1);
+ }
+ break;
+
+
+ case 'f':
+
+ /* read font family */
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -f illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(font_option, "usage: -f<font_family>", FALSE);
+ break;
+
+
+ case 's':
+
+ /* read font size */
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -s illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(size_option, "usage: -s<size>", FALSE);
+ break;
+
+
+ case 'v':
+
+ /* read line spacing */
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -v illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(line_option, "usage: -v<line_spacing>", FALSE);
+ break;
+
+
+ case 't':
+
+ /* read tab interval */
+ GetArg(tabin_option, "usage: -t<number>", TRUE);
+ if( tabin_option != NULL && sscanf(tabin_option,"%d",&tab_in) != 1 )
+ { fprintf(err_fp, "%s usage: -t<number>\n", ErrorHeader());
+ exit(1);
+ }
+ if( tab_in <= 0 )
+ { fprintf(err_fp, "%s -t: tab interval must be greater than 0\n",
+ ErrorHeader());
+ exit(1);
+ }
+ break;
+
+
+ case 'T':
+
+ /* read tab_out and tab_unit */
+ GetArg(tabout_option, "usage: -T<number><unit>", TRUE);
+ if( tabout_option != NULL )
+ { if( sscanf(tabout_option, "%f%c",&tab_out,&tab_unit) != 2 )
+ { fprintf(err_fp, "%s usage: -T<number><unit>\n", ErrorHeader());
+ exit(1);
+ }
+ if( tab_out <= 0 || tab_out >= 50 )
+ { fprintf(err_fp, "%s -T: unreasonably large or small tab interval\n",
+ ErrorHeader());
+ exit(1);
+ }
+ if( tab_unit != 'c' && tab_unit != 'i' && tab_unit != 'p' &&
+ tab_unit != 'm' && tab_unit != 'f' && tab_unit != 's' &&
+ tab_unit != 'v' )
+ { fprintf(err_fp, "%s -T: tab unit must be one of cipmfsv\n",
+ ErrorHeader());
+ exit(1);
+ }
+ tab_by_spacing = FALSE;
+ }
+ break;
+
+
+ case 'n':
+
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -n illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ headers_option = FALSE;
+ break;
+
+
+ case 'V':
+
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -V illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ fprintf(err_fp, "%s\n", PROG2LOUT_VERSION);
+ exit(0);
+ break;
+
+
+ case 'u':
+
+ if( raw_seen )
+ { fprintf(err_fp, "%s: -u illegal with -r option\n", ErrorHeader());
+ exit(1);
+ }
+ PrintUsage(err_fp);
+ exit(0);
+ break;
+
+
+ case 'l':
+
+ if( language_option != (char *) NULL )
+ { fprintf(err_fp, "%s: -l seen twice\n", ErrorHeader());
+ exit(1);
+ }
+ GetArg(language_option, "usage: -l<language>", FALSE);
+ for( i = 0; languages[i] != (LANGUAGE *) NULL; i++ )
+ {
+ for( j = 0; languages[i]->names[j] != (char *) NULL; j++ )
+ {
+ if( strcmp(languages[i]->names[j], language_option) == 0 )
+ break;
+ }
+ if( languages[i]->names[j] != (char *) NULL )
+ break;
+ }
+ if( languages[i] != (LANGUAGE *) NULL )
+ {
+ language = languages[i];
+ }
+ else
+ {
+ fprintf(err_fp, "%s: unknown language %s\n", ErrorHeader(),
+ language_option);
+ exit(1);
+ }
+
+ /* set style unless already done by a -p flag */
+ if( style_option == NO_STYLE )
+ {
+ if( strcmp(language->default_style, "fixed") == 0 )
+ { style_option = FIXED_STYLE;
+ }
+ else if( strcmp(language->default_style, "varying") == 0 )
+ { style_option = VARYING_STYLE;
+ tab_by_spacing = FALSE;
+ }
+ else if( strcmp(language->default_style, "symbol") == 0 )
+ { style_option = SYMBOL_STYLE;
+ tab_by_spacing = FALSE;
+ }
+ else
+ { fprintf(err_fp,
+ "%s internal error: bad default print style for language %s\n",
+ ErrorHeader(), language_option);
+ abort();
+ }
+ }
+ break;
+
+
+ default:
+
+ fprintf(err_fp, "%s: unknown command line flag %s\n", ErrorHeader(),
+ argv[i]);
+ exit(1);
+ break;
+
+ }
+ else
+ {
+ if( raw_seen )
+ { fprintf(err_fp, "%s: file parameter illegal with -r flag\n",
+ ErrorHeader());
+ exit(1);
+ }
+ if( DEBUG_SETUP )
+ fprintf(stderr, "file_names[%d++] = argv[%d] = %s\n",
+ file_count, arg_pos, argv[arg_pos]);
+ file_names[file_count++] = argv[arg_pos];
+ }
+ } /* for */
+
+ /* make sure we have a language */
+ if( language == (LANGUAGE *) NULL )
+ {
+ fprintf(err_fp, "%s: missing -l option\n", ErrorHeader());
+ exit(0);
+ }
+
+ /* do the actual work */
+ if( raw_seen )
+ {
+ /* check that input and output files are open */
+ if( in_fp == NULL )
+ in_fp = stdin;
+ if( out_fp == NULL )
+ { fprintf(err_fp, "%s -r: missing -o option\n", ErrorHeader());
+ exit(1);
+ }
+
+ /* process the file */
+ SetupTokens(language, err_fp);
+ line_pos = 1;
+ curr_line[line_pos] = '\n'; /* forces line read */
+ line_num = 0;
+ NextChar(in_fp);
+ Process(language, in_fp, out_fp, err_fp, (TOKEN *) NULL);
+ }
+ else if( file_count > 0 )
+ {
+ int ch;
+ char *style_str, *font_str, *size_str, *line_str, *face_str;
+ char *tabin_str, *tabout_str;
+
+ /* sort out the options' values */
+ switch( style_option )
+ {
+
+ case FIXED_STYLE:
+
+ style_str = "fixed";
+ face_str = "Base";
+ font_str = font_option != NULL ? font_option : "Courier";
+ size_str = size_option != NULL ? size_option : "9p";
+ line_str = line_option != NULL ? line_option : "1.1fx";
+ tabin_str = tabin_option != NULL ? tabin_option : "8";
+ tabout_str = tabout_option != NULL ? tabout_option : "8s";
+ break;
+
+
+ case NO_STYLE:
+ case VARYING_STYLE:
+
+ style_str = "varying";
+ face_str = "Slope";
+ font_str = font_option != NULL ? font_option : "Times";
+ size_str = size_option != NULL ? size_option : "10p";
+ line_str = line_option != NULL ? line_option : "1.1fx";
+ tabin_str = tabin_option != NULL ? tabin_option : "8";
+ tabout_str = tabout_option != NULL ? tabout_option : "3f";
+ break;
+
+
+ case SYMBOL_STYLE:
+
+ style_str = "symbol";
+ face_str = "Slope";
+ font_str = font_option != NULL ? font_option : "Times";
+ size_str = size_option != NULL ? size_option : "10p";
+ line_str = line_option != NULL ? line_option : "1.1fx";
+ tabin_str = tabin_option != NULL ? tabin_option : "8";
+ tabout_str = tabout_option != NULL ? tabout_option : "3f";
+ break;
+
+
+ default:
+
+ fprintf(err_fp, "%s internal error in -p option\n", ErrorHeader());
+ abort();
+ break;
+ }
+
+ /* make sure we have an output file */
+ if( out_fp == (FILE *) NULL )
+ out_fp = stdout;
+
+ /* print the initial @Use clauses etc.*/
+ fprintf(out_fp, "%s%s\n", "@Sy", "sInclude { progf }");
+ fprintf(out_fp, "%s%s\n", "@Sy", "sInclude { doc }");
+ fprintf(out_fp, "@Use { @ProgSetup\n");
+ fprintf(out_fp, " language { %s }\n", language_option);
+ fprintf(out_fp, " style { %s }\n", style_str);
+ fprintf(out_fp, " %sfont { %s }\n", style_str, font_str);
+ fprintf(out_fp, " %ssize { %s }\n", style_str, size_str);
+ fprintf(out_fp, " %sline { %s }\n", style_str, line_str);
+ fprintf(out_fp, " %stabin { %s }\n", style_str, tabin_str);
+ fprintf(out_fp, " %stabout { %s }\n", style_str, tabout_str);
+ fprintf(out_fp, "}\n");
+ fprintf(out_fp, "@Document\n");
+ fprintf(out_fp, " @InitialFont { \"%s\" \"%s\" \"%s\" }\n",
+ font_str, face_str, size_str);
+ fprintf(out_fp, " @InitialBreak { lines \"%s\" nohyphen }\n", line_str);
+ fprintf(out_fp, "//\n");
+ fprintf(out_fp, "%s%s\n", "@Text @Be", "gin");
+
+ /* print each file, possibly with a header */
+ for( i = 0; i < file_count; i++ )
+ {
+ /* open file and initialize file position */
+ in_fp = fopen(file_names[i], "r");
+ if( in_fp == NULL )
+ { fprintf(err_fp, "%s: skipping input file %s (cannot open)\n",
+ ErrorHeader(), file_names[i]);
+ continue;
+ }
+ strcpy(file_name, file_names[i]);
+
+ /* print @NP if not first, and header if required */
+ if( i > 0 )
+ fprintf(out_fp, "\n\n@NP\n\n");
+ if( headers_option )
+ fprintf(out_fp, "{ Times Bold \"+3p\" } @Font \"%s\"\n@DP\n",
+ file_names[i]);
+
+ /* print file name and contents (don't format, let Lout call back) */
+ /* this string has been disguised so as not to fool progwlout! */
+ fprintf(out_fp, "%s%s%s\n", "@P", "rog @Be", "gin");
+ while( (ch = getc(in_fp)) != EOF )
+ putc(ch, out_fp);
+ fprintf(out_fp, "%s%s%s\n", "@E", "nd @P", "rog");
+ }
+
+ /* finish off whole input */
+ fprintf(out_fp, "%s%s%s\n", "@E", "nd @T", "ext");
+
+ }
+ exit(0);
+} /* end main */