diff options
Diffstat (limited to 'prg2lout.c')
-rw-r--r-- | prg2lout.c | 1832 |
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 */ |