/*
* Copyright %%\copyright%% 1980 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* lgrind --- general purpose "pretty printer" for use with %%\LaTeX%%.
* A %%\LaTeX%% version of tgrind, which is a %%\TeX%% version
* of vgrind. Used to "grind nice program listings."
*
* Copyright %%\copyright%% 1985 by Van Jacobson, Lawrence Berkeley Laboratory
* This program may be freely used and copied but %%{\bf may not be sold}%%
* without the author's %%{\bf written permission}%%. This notice must remain
* in any copy or derivative.
*
* This program is an adaptation of "vfontedpr" v4.2 (12/11/84) from
* the 4.2bsd Unix distribution. Vfontedpr was written by Dave
* Presotto (based on an earlier program of the same name written by
* Bill Joy).
*
* I would welcome comments, enhancements, bug fixes, etc. Please
* mail them to:
* van@@lbl-rtsg.arpa (from arpanet, milnet, csnet, etc.)
* ..!ucbvax!lbl-csam!van (from Usenet/UUCP)
*/
/*%%\par{\bf Thank you very much, Van. Due to the ``may not be sold'' clause%%*/
/*%%this program has become non-free. And since the author of this licence%%*/
/*%%cannot be contacted (address too old, apparently newer ones exist, but%%*/
/*%%no answers from them), this cannot be changed.}\par%%*/
/* Copyright %%\copyright%% 1995-99 by Michael Piefel
*
* Modifications.
* --------------
* 10 Feb 85 Van Written.
* 29 Mar 85 Chris Torek (chris@@maryland): Bug fixes for %|~|% and
* %|^L|% output. Most cpu-time eaters recoded
* to improve efficiency.
* 30 Mar 85 Chris & Van Fixed %|\C|% & %|\S|% (comment & string start
* indicators to really appear at the start of
* comments & strings. Changes for speeded-up
* @expmatch()@.
* 8 Oct 87 JSL Modified so as to compile on VMS. %|-i|% option
* Jan 88 JSL %|-e|% option for embedded code.
* Sep 91 George V Reilly Reformated and cleaned up code, including
* naughtiness with @NULL@. Added %|@|%,
* %|%%|%, %|%$|%, and %%\tt \%|%% features.
* Also the %|%<|%, %|%!|%, and %|%#|% features.
* Oct 94 Matthias Eckermann (%|ud311aa@@sunmail.lrz-muenchen.de|%)
* fixed a little bug, added: Internal_Help,
* Sep 95 Michael Piefel Modified for %%\LaTeXe%%
* Feb 96 Michael Piefel Restructured into ANSI C
* May 99 Michael Piefel Made it Y2K compliant
* Oct 99 Michael Piefel Space runs make tabstops
* Dec 99 Michael Piefel Version option, space allowed after '-l'
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
/* One of the following two (depending on your system) */
#include <unistd.h>
/* #include <io.h> */
#include "lgrindef.h"
#include "regexp.h"
#ifndef vms
# include <sys/types.h>
# include <sys/stat.h>
#else
# include <types.h>
# include <stat.h>
#endif
#define STANDARD 0
#define ALTERNATE 1
#define NOTCODE 0 /* the three states of @incode@ */
#define INITCODE 1
#define OTHERCODE 2
#define PNAMELEN 80 /* length of a function/procedure name */
#define PSMAX 20 /* size of procedure name stacking */
boolean incomm; /* in a comment of the primary type */
boolean instr; /* in a string constant */
boolean inchr; /* in a character constant */
int incode; /* in program text within a comment */
int latexcode; /* in program text within %%\LaTeX%% */
int latex_tt; /* in %|\texttt|% text within %%\LaTeX%% */
boolean use_tt; /* use %|\texttt|% everywhere */
boolean do_at; /* pay attention to %|@|%s in %%\LaTeX%% */
boolean do_tt; /* pay attention to %|||%s in %%\LaTeX%% */
boolean nokeyw = FALSE; /* no keywords being flagged */
boolean prccont; /* continue last procedure */
boolean code_cmnts = TRUE; /* Treat %|@|%, etc specially in comments */
boolean code_latex = TRUE; /* Treat %|@|%, etc specially in %%\LaTeX%% */
int lastout; /* (extended) last character to outchar */
int comtype; /* type of comment */
int psptr; /* the stack index of the current procedure */
char pstack[PSMAX][PNAMELEN+1]; /* the procedure name stack */
int plstack[PSMAX]; /* the procedure nesting level stack */
int blklevel; /* current nesting level */
int reclevel; /* current @record@ nesting level */
int procedures_pending; /* proc starts w/o block starts */
char fname[200]=""; /* File being read */
int lineno; /* Line number in that file */
int linenocode; /* Line number of the source code worked on */
char pname[BUFFERSIZE+1]; /* Current procedure name */
/*
* The language specific globals
*/
char defsbuf[200]="DeFsBuF"DEFSFILE;
char *defsfile=&defsbuf[7]; /* name of language definitions file */
char language[PNAMELEN]="mPi"; /* the language indicator */
char the_buf[BUFFERSIZE+1]; /* general purpose buffer */
char *buf = the_buf + 1; /* @buf-1@ must be valid */
char *strings; /* store the keywords */
char *defs; /* language definitions from lgrindef */
char preamble[BUFFERSIZE/4]; /* first preamble */
char postamble[BUFFERSIZE/4]; /* first preamble */
char preamble2[BUFFERSIZE/4]; /* file preamble */
char config[BUFFERSIZE/4]; /* redefinitions within lgrind-environment */
char chartab[BUFFERSIZE/4]; /* buffer for chartab modifications */
char *l_keywds[BUFFERSIZE/2]; /* keyword table address */
char *l_prcbeg; /* regular expr for procedure begin */
char *l_noproc; /* regexp for lines with NO procedure begin */
char *l_combeg; /* regexp introducing a comment */
char *l_comend; /* regexp ending a comment */
char *l_acmbeg; /* regexp introducing a comment */
char *l_acmend; /* regexp ending a comment */
char *l_blkbeg; /* regexp beginning of a block */
char *l_blkend; /* regexp ending a block */
char *l_strbeg; /* regexp starting string constant */
char *l_strend; /* regexp ending string constant */
char *l_chrbeg; /* regexp starting character constant */
char *l_chrend; /* regexp ending character constant */
char *l_cdebeg; /* regexp starting prog text within comment */
char s_cdebeg[BUFFERSIZE/8]; /* actual string corresponding to @l_cdebeg@ */
char *l_cdeend; /* regexp ending prog text within comment */
char *l_texbeg; /* regexp starting %%\TeX%% text in comment */
char *l_texend; /* regexp ending %%\TeX%% text in comment */
char *l_txmbeg; /* regexp starting %%\TeX%% math in comment */
char *l_txmend; /* regexp ending %%\TeX%% math in comment */
char *l_tt_beg; /* regexp starting verbatim text in comment */
char *l_tt_end; /* regexp ending typewriter text in comment */
char *l_at; /* regexp for %|@|% in %%\LaTeX%% text */
char *l_tt; /* regexp for %|||% in %%\LaTeX%% text */
char *l_pc; /* regexp for %|%|% in %%\LaTeX%% text */
char *l_id; /* string containing valid identifier chars */
char *l_record; /* start of lexical block outside functions */
char l_escape; /* character used to escape characters */
boolean l_toplex; /* procedures only defined at top lex level */
boolean l_onecase; /* upper & lower case equivalent */
boolean l_cfunctions; /* special for C function search */
boolean embed = FALSE; /* -e seen --- do embedded code */
boolean code = TRUE; /* Looking at code */
int TabWidth = 8; /* Default width of a tab */
/*
* global variables also used by expmatch
*/
extern boolean _escaped; /* if last character was an escape */
extern char *_sstart; /* start of the current string */
extern int (*re_strncmp)(const char*, const char*, size_t);
/* function to do string compares */
int main(int argc, char **argv)
{
struct stat stbuf;
boolean hseen = FALSE; /* -h seen */
char *hstring=""; /* header string to use */
char *programname; /* the zeroeth argument */
boolean iseen = FALSE; /* -i seen */
int files_done = 0;
/* take input from the standard place */
if (!strcmp(argv[0], "-")) {
argc = 0;
goto rest;
}
/* put output to non-standard place */
if (!strcmp(argv[0], "-o")) {
if (freopen(argv[1], "w", stdout) == NULL) {
perror(argv[1]);
return BADEXIT;
}
argc--, argv++;
argc--, argv++;
continue;
}
/* Process embedded text */
if (!strcmp(argv[0], "-e")) {
embed = TRUE;
argc--, argv++;
continue;
}
/* format for inclusion */
if (!strcmp(argv[0], "-i")) {
iseen = TRUE;
argc--, argv++;
continue;
}
/* indicate no keywords */
if (!strcmp(argv[0], "-n")) {
nokeyw++;
argc--, argv++;
continue;
}
/* Don't treat %|@|%, etc. specially in comments */
if (!strcmp(argv[0], "-c")) {
code_cmnts = FALSE;
argc--, argv++;
continue;
}
/* Do treat %|@|%, etc. specially in comments */
if (!strcmp(argv[0], "+c")) {
code_cmnts = TRUE;
argc--, argv++;
continue;
}
/* Don't treat %|@|%, etc. specially in
* %%\LaTeX%% text (@embed@ only) */
if (!strcmp(argv[0], "-a")) {
code_latex = FALSE;
argc--, argv++;
continue;
}
/* Do treat %|@|%, etc. specially in
* %%\LaTeX%% text (@embed@ only) */
if (!strcmp(argv[0], "+a")) {
code_latex = TRUE;
argc--, argv++;
continue;
}
/* Use %|\texttt|% for all fonts */
if (!strcmp(argv[0], "-t")) {
sscanf(argv[1], "%d", &TabWidth);
argc--, argv++;
argc--, argv++;
continue;
}
/* specify the language */
if (!strncmp(argv[0], "-l", 2)) {
if (strlen(argv[0])==2) {
strcpy(language, argv[1]);
argc--, argv++;
argc--, argv++;
} else {
strcpy(language, argv[0]+2);
argc--, argv++;
continue;
}
}
/* specify the language description file for permanent use */
if (!strcmp(argv[0], "-d!")) {
writeDefsfileInExe(programname, argv[1]);
return OKEXIT;
}
/* specify the language description file */
if (!strncmp(argv[0], "-d", 2)) { /* TODO strncmp? */
strcpy(defsfile, argv[1]);
argc--, argv++;
argc--, argv++;
continue;
}
/* specify the file containing variable substitutions */
if (!strcmp(argv[0], "-v")) {
varsubstlist=parsevartab(argv[1]);
argc--, argv++;
argc--, argv++;
continue;
}
/* open the file for input */
if (freopen(argv[0], "r", stdin) == NULL) {
perror(argv[0]);
return BADEXIT;
}
strcpy(fname, argv[0]);
argc--, argv++;
}
rest:
lineno = 0;
setpreambles();
/* get config data from %|lgrindef|% */
if (tgetent(buf, "firstpreamble", defsfile)>0)
parsepreamble(preamble);
if (tgetent(buf, "postamble", defsfile)>0)
parsepreamble(postamble);
if (tgetent(buf, "filepreamble", defsfile)>0)
parsepreamble(preamble2);
if (tgetent(buf, "configuration", defsfile)>0)
parsepreamble(config);
if (tgetent(buf, "chartab", defsfile)>0)
parsechartab();
if (iseen && embed) {
fprintf(stderr, "-i makes no sense with -e; -e ignored\n");
embed = FALSE;
}
if (!iseen && !embed) {
if (files_done == 0) printpreamble(preamble);
printpreamble(preamble2);
printf("\\begin{lgrind}\n");
printpreamble(config);
/* In case \BGfont was modified in config */
printf("\\BGfont\n");
}
if (embed)
printf("%% This document was generated automagically by lgrind. DO NOT EDIT.\n\n");
if (iseen)
{
printf("%% Remember to use the lgrind style\n\n");
printf("\\Head{");
if (hseen) putstr(hstring);
printf("}\n");
}
if (!iseen && !embed) {
printf("\\end{lgrind}\n");
}
if (code)
fprintf(stderr, "Reached EOF within code in file %s\n", fname);
} while (argc > 0);
if (!iseen && !embed) {
printpreamble(postamble);
}
return OKEXIT;
}
/*
* @readfile()@ --- read and process a file
*/
void readfile(FILE *fp)
{
register char *cp;
boolean LGinline=FALSE; /* Doing %( -- %) */
char term; /* ']' or ')' */
char *atptr; /* start of %|@|% within %%\LaTeX%% text */
char *atendptr; /* end of %|@|% within %%\LaTeX%% text */
char *pcptr; /* start of %|%|% within %%\LaTeX%% text */
char *pcendptr; /* end of %|%|% within %%\LaTeX%% text */
char temp[BUFFERSIZE];
char fnamebak[200];
int linenobak;
/* Change the language */
if (buf[1] == '#') {
if (*cp == '\n')
fprintf(stderr, "no language seen after %%# in file\
%s at line %d\n", fname, lineno);
else {
cp[strlen(cp) - 1] = '\0'; /* nuke the @'\n'@ */
while ((*cp==' ') || (*cp=='\t')) cp++;
strcpy(language, cp);
printf("%% switching to %s\n", language);
setlang();
}
continue;
}
/* Turn %|@|% or %|||% processing within %%\LaTeX%%
* text on or off. */
if (buf[1] == '@' || buf[1] == '|') {
if (*cp == '\n')
fprintf(stderr, "no setting seen after %%%c in file\
%s at line %d\n", buf[1], fname, lineno);
else {
int flag = (*cp == '1' || *cp == '+');
if (buf[1] == '@')
do_at = flag;
else
do_tt = flag;
}
continue;
}
code = TRUE;
/* take input from another file or from a shell command */
if (buf[1] == '<' || buf[1] == '!') {
FILE *fp;
char source = buf[1];
/*
* We're in embedded code.
*/
if (buf[0] == '%') {
if (buf[1] == '*') {
if (term != ']')
fprintf(stderr, "%%* only makes sense in display mode\
in file %s at line %d\n", fname, lineno);
else {
printf("\\endlgrinde\\LGend\n");
printf("\\LGinlinefalse\\LGbegin\\lgrinde[%d]\n", linenocode+1);
}
continue;
} else if (buf[1] == term) {
if (term == ')') printf("\\egroup");
if (buf[2] == '\n')
printf("\\endlgrinde\\LGend\n");
else
printf("\\endlgrinde%s", &buf[2]);
code = FALSE;
continue;
} else if (buf[1] == '=') { /* Send literal */
fputs(&buf[2], stdout);
continue;
} else if (buf[1] == '[' || buf[1] == '('
|| buf[1] == ']' || buf[1] == ')') {
fprintf(stderr,
"Possible nested embedded code in file %s at line %d\n",
fname, lineno);
}
}
/*
* Inline code --- suppress leading whitespace
*/
if (LGinline) {
while (isspace(*cp))
cp++;
}
} /* @if (embed)@ */
if (*cp == '\f') {
printf("\\NewPage\n");
cp++;
if (*cp == '\n') /* some people like ^Ls on their own line */
continue;
}
prccont = FALSE;
linenocode++;
putScp(cp);
if (!embed && prccont && (psptr >= 0)) {
printf("\\ProcCont{");
putstr(pstack[psptr]);
printf("}");
}
#ifdef DEBUG
printf("com %o str %o chr %o ptr %d\n", incomm, instr, inchr, psptr);
#endif
} /* @while fgets()@ */
}
/*
* Set all of the language-dependent variables
*/
void setlang(void)
{
char *p, *cp;
int i;
/*
* get the language definition from the defs file
*/
if (strcmp(language, "mPi") == 0) /* none yet defined */
{
i = tgetent(defs, "extensions", defsfile);
if (i == 0) strcpy(language, "c");
else
{
cp = strrchr(fname, '.');
if (cp !=NULL)
{
p=buf; cp = tgetstr(cp+1, &p);
}
if (cp != NULL) strcpy(language, cp);
else strcpy(language, "c");
}
}
i = tgetent(defs, language, defsfile);
if (i == 0) {
fprintf(stderr, "no entry for language %s\n", language);
exit(BADEXIT);
} else if (i < 0) {
fprintf(stderr, "cannot find lgrindef file `%s'\n", defsfile);
exit(BADEXIT);
}
p = strings;
if (tgetstr("kw", &p) == NULL)
nokeyw = TRUE;
else {
char **cpp;
/*
* Write out a line of TeX in comment verbatim
*/
void putVtc(char *stringstart, char *comacmptr)
{
char *texptr, *texendptr;
char texline[BUFFERSIZE/8];
texendptr = expmatch(stringstart, l_texend, &texptr, (char *) NULL);
if (texendptr == comacmptr)
{
strncpy(texline, stringstart, texptr-stringstart);
texline[texptr-stringstart]='\0';
printf("%s\n", texline);
}
}
/*
* Write out a formatted line of program text
*/
void putScp(char *os)
{
register char *s = os; /* pointer to unmatched string */
register char *s1; /* temp. string */
char *comptr; /* start of a comment delimiter */
char *comendptr; /* end of a comment delimiter */
char *acmptr; /* start of an alt. comment delimiter */
char *acmendptr; /* end of an alt. comment delimiter */
char *strptr; /* start of a string delimiter */
char *strendptr; /* end of a string delimiter */
char *chrptr; /* start of a char. const delimiter */
char *chrendptr; /* end of a char. const delimiter */
char *cdeptr; /* start of prog text delim within a comment */
char *cdeendptr; /* end of prog text delim within a comment */
char *cdbptr; /* start of prog text delim within a comment */
char *cdbendptr; /* end of prog text delim within a comment */
char *texptr; /* start of %%\TeX%% text delim within a comment */
char *texendptr; /* end of %%\TeX%% text delim within a comment */
char *txmptr; /* start of %%\TeX%% math delim within a comment */
char *txmendptr; /* end of %%\TeX%% math delim within a comment */
char *tt_ptr; /* start of typewriter delim within a comment */
char *tt_endptr; /* end of typewriter delim within a comment */
char *blksptr; /* start of a lexical block start */
char *blksendptr; /* end of a lexical block start */
char *recsptr; /* start of a lexical block outside functions start */
char *recsendptr; /* end of a lexical block outside functions start */
char *blkeptr; /* start of a lexical block end */
char *blkeendptr; /* end of a lexical block end */
_sstart = os; /* remember the start for @expmatch()@ */
_escaped = FALSE;
if (nokeyw || incomm || instr)
goto skip;
/* end of a lexical block */
if (blkeptr != NULL) {
if ((blksptr == NULL || blkeptr < blksptr) &&
(recsptr == NULL || blkeptr < recsptr)) {
putKcp(s, blkeendptr - 1, FALSE);
s = blkeendptr;
if (blklevel) blklevel--;
if (reclevel) reclevel--;
else if (psptr >= 0 && plstack[psptr] >= blklevel) {
/* end of current procedure */
blklevel = plstack[psptr];
/* see if we should print the last proc name */
if (--psptr >= 0)
prccont = TRUE;
else
psptr = -1;
}
continue;
}
}
/* start of a lexical block */
if (blksptr != NULL) {
putKcp(s, blksendptr - 1, FALSE);
s = blksendptr;
if (procedures_pending)
procedures_pending--;
else
/* when C functions then not on top level */
if (!l_cfunctions || blklevel)
blklevel++;
continue;
}
/* start of a lexical block outside functions */
if (recsptr != NULL) {
putKcp(s, recsendptr - 1, FALSE);
s = recsendptr;
if (procedures_pending)
procedures_pending--;
else
blklevel++;
reclevel++;
continue;
}
/* check for end of comment */
} else if (incomm) {
/* Check for program text within comment */
if (cdeptr != NULL
&& (texptr == NULL || cdeptr < texptr)
&& (tt_ptr == NULL || cdeptr < tt_ptr)
&& (txmptr == NULL || cdeptr < txmptr)) {
putKcp(s, cdeptr-1, TRUE);
printf("\\CE{}");
s = cdeendptr;
incode = INITCODE; incomm = FALSE;
continue;
}
/* Check for %%\TeX%% text within comment */
if (texptr != NULL
&& (tt_ptr == NULL || texptr < tt_ptr)
&& (txmptr == NULL || texptr < txmptr)) {
putKcp(s, texptr-1, TRUE);
s = texendptr;
if ((texendptr =
expmatch(s, l_texend, &texptr, (char *) NULL)) != NULL) {
putchar('{'); putVcp(s, texptr-1); putchar('}');
s = texendptr;
} else {
fprintf(stderr, "LaTeX text within a comment must all be\
on one line (file %s at line %d)\n", fname, lineno);
s += strlen(s);
}
continue;
}
/* Check for typewriter text within comment */
if (tt_ptr != NULL
&& (txmptr == NULL || tt_ptr < txmptr)) {
putKcp(s, tt_ptr-1, TRUE);
s = tt_endptr;
if ((tt_endptr =
expmatch(s, l_tt_end, &tt_ptr, (char *) NULL)) != NULL) {
printf("{\\TTfont ");
latex_tt=TRUE;
putKcp(s, tt_ptr-1, TRUE);
latex_tt=FALSE;
printf("}");
s = tt_endptr;
} else {
fprintf(stderr, "typewriter text within a comment must all\
be on one line (file %s at line %d)\n\t%s",
fname, lineno, s);
s += strlen(s);
}
continue;
}
/* Check for %%\TeX%% math within comment */
if (txmptr != NULL) {
putKcp(s, txmptr-1, TRUE);
s = txmendptr;
if ((txmendptr =
expmatch(s, l_txmend, &txmptr, (char *) NULL)) != NULL) {
putchar('$'); putVcp(s, txmptr-1); putchar('$');
s = txmendptr;
} else {
fprintf(stderr, "TeX math within a comment must all be\
on one line (file %s at line %d)\n", fname, lineno);
s += strlen(s);
}
continue;
}
} /* @if (code_cmnts)@ */
/* check for end of string */
} else if (instr) {
if ((strendptr =
expmatch(s, l_strend, (char **) NULL, (char *) NULL)) != NULL) {
putKcp(s, strendptr-1, TRUE);
s = strendptr;
instr = FALSE;
printf("\\SE{}");
} else {
strptr = s;
s += strlen(s);
putKcp(strptr, s-1, TRUE);
}
continue;
/* check for end of character string */
} else if (inchr) {
if ((chrendptr =
expmatch(s, l_chrend, (char **) NULL, (char *) NULL)) != NULL) {
putKcp(s, chrendptr-1, TRUE);
s = chrendptr;
inchr = FALSE;
printf("\\SE{}");
} else {
chrptr = s;
s += strlen(s);
putKcp(chrptr, s-1, TRUE);
}
continue;
}
/* print out the line */
chrptr = s;
s += strlen(s);
putKcp(chrptr, s-1, FALSE);
} while (*s);
}
/*
* Output a %%\LaTeX%% command to tab to column "col" (see the documentation
* for a partial explanation of the bizarre brace arrangement).
*/
#define tabto(col) printf("}\\Tab{%d}{", col);
/*
* @islidchr()@ is @TRUE@ for things that can begin identifiers;
* @isidchr@ is @TRUE@ of identifier constituents.
*/
#define islidchr(c)(((isalpha(c) || (strchr(l_id, c))) && c!=0))
#define isidchr(c)(((isalnum(c) || (strchr(l_id, c))) && c!=0))
/*
* Get the identifier starting at @s@. It is assumed that @s@ may indeed
* start an identifier. Value is %$>0$% for a keyword --- the count of
* characters in the keyword --- and %$<0$% if not a keyword, in which
* case the value returned is the negative of the number of bytes in
* the matched identifier.
*
* This function checks @nokeyw@ and won't check to see if the
* identifier found is a keyword if it is @TRUE@.
*/
int getid(char *s)
{
char **ss = l_keywds;
int i = 1;
char *cp = s;
int firstc = *s;
while (++cp, isidchr(*cp))
i++;
if (nokeyw)
return -i;
while ((cp = *ss++) != '\0') {
if (!l_onecase && firstc != *cp)
continue;
if ((*re_strncmp)(s, cp, i) == 0 && !isidchr(cp[i]))
return i;
}
return -i;
}
/*
* Calculate the width of a string, including tabs
*/
int width(register char *s, register char *os)
{
register int i = 0, c;
while (s < os) {
c = *s++;
if (c == '\t') {
/* i = (i + 8) &~ 7; */
i = ((i + TabWidth) / TabWidth) * TabWidth;
continue;
}
if (c < ' ')
i += 2;
else
i++;
}
return i;
}
/*
* Write out a portion of the line
*/
boolean putKcp(char *start, char *end, boolean nix)
/* start Start of string to write
end End of string to write
nix Don't look for identifiers, numbers
returns whether we ate only as much as we ought to */
{
int i, c;
while (start <= end) {
c = *start++;
/*
* take care of nice tab stops ...
* ... and of space runs
*/
if (c == '\t' || (c == ' ' && *start == ' ')) {
while (start <= end && ( *start == '\t' || *start == ' ' ))
start++;
tabto(width(_sstart, start));
continue;
}
/*
* First split off numbers. We have a rather ad hoc
* definition: A number is a digit followed by any number
* of digits or letters, and periods.
* (Additionally a number can start with %|$|% (e.g. hex numbers).)
* This produces meaningless parses --- %$.2$% is parsed as %|.|%
* followed by the number %|2|% --- but should produce decent
* results for most languages (outside of maybe FORTRAN and DCL).
*/
if (!nix) {
if (c == '#' || islidchr(c)) {
i = getid(--start);
if (i > 0)
printf("\\K{");
else {
printf("\\V{");
i = substvarname(&start, -i);
}
while (--i >= 0) {
c = *start++;
outchar(c);
}
putchar('}');
continue;
}
else if (isdigit(c) || c == '$') {
printf("\\N{");
do {
if (c == 'l')
printf("$\\ell$");
else
outchar(c);
c = *start++;
} while (isalnum(c) || (c == '.' && *start != '.'));
putchar('}');
start--;
continue;
}
}
outchar(c);
}
return (start-1)==end;
}
/*
* Write out a portion of the line verbatim
*/
void putVcp(char *start, char *end)
/* start Start of string to write
end End of string to write */
{
for ( ; start <= end; start++) {
putchar(*start);
}
}
/*
* Output a string, escaping special characters
*/
void putstr(register char *cp)
{
register int c;
if (cp == NULL)
return;
while ((c = *cp++) != '\0')
outchar(c);
}
/*
* The following table converts ASCII characters to a printed
* representation, taking care of all the %%\LaTeX%% quoting. N.B.: all
* single-character strings are assumed to be equivalent to the
* character for that index (i.e., @printtab['c']@ can't be @"f"@).
* (This is purely for efficiency hacking.)
*
* Notes:
* Some pairs of characters are handled specially within @outchar()@;
* this table contains no indication when that happens.
* %|`|% is output as %|{`}|% to avoid various ligatures, such as %|!`|%.
* The %|''|% and %|--|% (and %|---|%) ligatures are not a problem
* because those characters are output as macros anyway. Using %|{`}|%
* is sufficient since nothing we put out will ever be given to the
* hyphenation algorithms anyway.
*
* @ttprinttab@ is the same for the emulated verbatim mode
*/
#define EMDASH (-1)
/*
* Output one character, fixed up as required. Since there is no call-back
* to tell @outchar()@ to flush what it has stored, it must always produce
* some output --- it can't simply retain state. This makes certain
* translations impossible, but we can live without them for now....
*/
void outchar(int c)
{
if (!latex_tt)
{
if (c == ' ') {
putchar('_');
goto fin;
}
switch (lastout) {
case '.':
case '|':
if (c == lastout)
printf("\\,");
break;
case ' ':
#if 0 /* gvr hates this trick */
if (incomm && c == '-') {
printf("---");
c = EMDASH; /* For future cleverness... */
goto fin;
}
#endif
break;
case '[':
if (c == ']')
printf("\\,");
break;
case '-':
if (c == '>')
printf("\\!");
break;
case '<':
if (c == '-')
printf("\\!");
break;
}
if (incomm && c == '-') {
putchar('-');
goto fin;
}
printtab[(unsigned char)c][1] ?
printf("%s", printtab[(unsigned char)c]) : putchar(c);
fin:
lastout = c;
}
else
c==32 ? putchar('~') :
ttprinttab[(unsigned char)c][1] ?
printf("\\symbol%s", ttprinttab[(unsigned char)c]) : putchar(c);
}
/*
* Look for a procedure beginning on this line
*/
boolean isprocNorm(char *s)
{
pname[0] = '\0';
if ((!l_toplex || blklevel == 0)
&& expmatch(s, l_prcbeg, (char **) NULL, pname) != NULL
&& expmatch(s, l_noproc, (char **) NULL, (char *) NULL) == NULL)
{
procedures_pending++;
blklevel++;
return TRUE;
}
return FALSE;
}
/* Special version of the above for C functions */
boolean isprocC(char *s)
{
char cheat[BUFFERSIZE];
char *j=s, *i=cheat;
boolean comm=FALSE, string=FALSE, schar=FALSE;
int brack=0;
if (blklevel!=0) return FALSE;
while (*j)
{
if (*j=='"' && !comm && !schar) string=(string+1)%2;
else if (*j=='\'' && !comm && !string) schar=(schar+1)%2;
else if (*j=='\\' && !comm) j++;
else if (!comm && !string && !schar && *j=='{') brack++;
else if (!comm && !string && !schar && *j=='}') brack--;
else if (!comm && !string && !schar && *j=='#') break;
else if (!comm && !string && !schar && *j=='/' && *(j+1)=='/') break;
else if (!comm && !string && !schar && *j=='/' && *(j+1)=='*') comm=TRUE;
else if ( !string && !schar && *j=='*' && *(j+1)=='/')
{comm=FALSE; j++;}
else if (!brack && !comm && !string && !schar)
*i++=*j;
j++;
}
*i = '\0';
return isprocNorm(cheat);
}
/* The VMS code below has been absolutely untested for the last few years.
* Don't ask me about it. Please. MPi
*/
/*
* @getredirection()@ is intended to aid in porting C programs
* to VMS (Vax-11 C) which does not support %|>|% and %|<|%
* I/O redirection. With suitable modification, it may
* useful for other portability problems as well.
*
* Modified, 24-Jan-86 by Jerry Leichter
* When creating a new output file, force the maximum record size to
* 512; otherwise, it ends up as 0 (though the C I/O system won't write
* a record longer than 512 bytes anyway) which will cause problems if
* the file is later opened for @APPEND@ --- if the maximum record size
* is 0, C will use the length of the longest record written to the file
* for its buffer!
*/
#ifdef vms
# include <stdio.h>
# include <errno.h>
int
getredirection(argc, argv)
int argc;
char **argv;
/*
* Process VMS redirection args. Exit if any error is seen.
* If @getredirection()@ processes an argument, it is erased
* from the vector. @getredirection()@ returns a new @argc@ value.
*
* Warning: do not try to simplify the code for VMS. The code
* presupposes that @getredirection()@ is called before any data is
* read from @stdin@ or written to @stdout@.
*
* Normal usage is as follows:
*
*@ main(argc, argv)
* int argc;
* char *argv[];
* {
* argc = getredirection(argc, argv);
* }@
*/
{
register char *ap; /* Argument pointer */
int i; /* @argv[]@ index */
int j; /* Output index */
int file; /* File_descriptor */
for (j = i = 1; i < argc; i++) { /* Do all arguments */
switch (*(ap = argv[i])) {
case '<': /* %|<file|% */
if (freopen(++ap, "r", stdin) == NULL) {
perror(ap); /* Can't find file */
exit(errno); /* Is a fatal error */
}
break;
case '>': /* %|>file|% or %|>>file|% */
if (*++ap == '>') { /* %|>>file|% */
/*
* If the file exists, and is writable by us,
* call @freopen()@ to append to the file (using the
* file's current attributes). Otherwise, create
* a new file with "vanilla" attributes as if
* the argument was given as %|>filename|%.
* @access(name, 2)@ is @TRUE@ if we can write on
* the specified file.
*/
if (access(++ap, 2) == 0) {
if (freopen(ap, "a", stdout) != NULL)
break; /* Exit @case@ statement */
perror(ap); /* Error, can't append */
exit(errno); /* After @access@ test */
} /* If file accessable */
}
/*
* On VMS, we want to create the file using "standard"
* record attributes. @create(...)@ creates the file
* using the caller's default protection mask and
* "variable length, implied carriage return"
* attributes. @dup2()@ associates the file with @stdout@.
*/
if ((file = creat(ap, 0, "rat=cr", "rfm=var", "mrs=512")) == -1
|| dup2(file, fileno(stdout)) == -1) {
perror(ap); /* Can't create file */
exit(errno); /* is a fatal error */
} /* If %|>|% creation */
break; /* Exit @case@ test */
default:
argv[j++] = ap; /* Not a redirector */
break; /* Exit @case@ test */
}
} /* For all arguments */
argv[j] = NULL; /* Terminate @argv[]@ */
return j; /* Return new @argc@ */
}