home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
texipf21.zip
/
texi2ipf
/
translat.c
< prev
Wrap
C/C++ Source or Header
|
1997-02-15
|
35KB
|
997 lines
/*
* translate.c - main guts of texi2ipf
*
* texi2roff history:
* Release 1.0a August 1988
* Release 2.0 January 1990
*
* Copyright 1988, 1989, 1990 Beverly A.Erlebacher
* erlebach@cs.toronto.edu ...uunet!utai!erlebach
*
* texi2ipf history:
* Release 1.0 February 1993
*
* Modified by Marcus Gröber, Fido 2:2402/61.1
*
* Modified by Martin "Herbert" Dietze, Email herbert@wiloyee.shnet.org
*
*/
/*
* History:
*
* $Log: translat.c,v $
* Revision 1.10 1997/02/06 12:45:18 herbert
* - Added documentation in Texinfo format.
* - Minor bug fixes.
*
* Revision 1.9 1997/02/04 13:05:43 herbert
* Menu descriptions of more than one lines are now converted correctly if they
* are indented by more than 25 whitespaces (1 tab == 8 spaces).
*
* Revision 1.8 1997/01/16 13:41:33 herbert
* Put stuff from translat.c to new file toolz.c.
* Fixed an error in the remove_texicmd() routine (correct handling of "@@",
* "@{" and "@}" tags).
*
* Revision 1.7 1997/01/16 10:45:14 herbert
* Added routine remove_texicmds() for deformatting automatically generated
* index entries.
*
* Revision 1.6 1997/01/15 13:34:19 herbert
* - Fixed the index entry generating for some @def* commands.
*
* Revision 1.5 1997/01/15 12:18:19 herbert
* - Fixed a bug in eat_first_word() that made the program crash sometimes.
* - "@ignore" environments will now be handled like real comments.
*
* Revision 1.4 1996/12/17 15:14:23 herbert
* Only some cosmetic changes. The code looks still rather ugly to me :-)
*
* Revision 1.3 1996/12/17 14:10:01 herbert
* Added support for pseudo-Texinfo-commands: @ifhtml (ignored) and @ipfline{}
* (my invention) for ptuting IPF code into the Texinfo source.
* Added @macro command to table.h, will be ignored.
*
* Revision 1.2 1996/12/04 12:02:20 herbert
* Added new target "commit" to makefile for easier generation of updated
* packages.
* Changed error messages for @...index commands to "ignore" message.
*
* Revision 1.1.1.1 1996/12/02 12:10:01 herbert
* Texi2IPF 1.0
*
*/
#include "texi2ipf.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
static char * id =
"@(#)$Id: translat.c,v 1.10 1997/02/06 12:45:18 herbert Exp $";
#define FONTSTRSIZE 40
extern jmp_buf cleanup_point;
extern int what[MAXILEVEL];
int displaylevel = 0; /* nesting level of 'display' text */
int inmacroarg = 0; /* protect roff macro args flag */
int ilevel = 0; /* nesting level of itemized lists */
int linecount;
int newpara=-1,nopara=0,firstitem=0;
int started = 0; /* text has not yet started */
int lastlevel = 1; /* level of last heading */
int vistext; /* set if visible text encountered */
char *filename;
char *inp; /* pointer into input buffer */
char lastnode[MAXLINELEN]="\0"; /* last node id */
char macroarg[MAXARG][MAXLINELEN]; /* buffor for xref arguments */
int argmode=-1;
int inside_menu = NO; /* We're inside of a menu block. */
int is_entry = 0;
int was_entry = 0;
int ignoring = NO;
int footnote_nr = 1; /* Actual footnote Number */
struct tablerecd * lookup( char *);
char * itemize( char *, char *);
char * doitem( char *, char *, int);
char * value( char *);
int setclear( char *, char *);
/* forward references */
void errormsg( char *, char *);
char * gettoken( char *, char *);
char * eatwhitespace( char *);
char * rtrim( char *);
char * interpret( char *, char *);
int translate_line( char *);
char * eat_first_word( char *);
int menu_entry_maybe( char *, char *);
void remove_texicmds( char *);
void translate_string( char*);
extern struct tablerecd tempframe, temp1stbrac, temp1stword;
/*
* translate - translate one Texinfo file
*/
int translate( FILE *in, char *inname)
{
char input[MAXLINELEN];
int savlinct;
char *savfilnam;
/*
* save global variables linecount and filename in case this is a
* recursive call to translate an @include file. these variables
* are used by errormsg() which is called from many places.
*/
savfilnam = filename;
savlinct = linecount;
filename = inname;
linecount = 0;
/*
* if fgets() truncates a line in the middle of a token, a blank will
* appear in the word containing the MAXLINELENth char in the absurdly
* long line. this is handled by gettoken()
*/
while ( fgets(input, MAXLINELEN, in) != NULL ) {
++linecount;
rtrim(input);
if ( *input == '\0' && started )
newpara = ilevel; /* remeber paragraph */
else
if( translate_line (input) )
return ERROR;
/* endif */
/* endif */
}/* while */
/* restore the globals */
filename = savfilnam;
linecount = savlinct;
return 0;
}/* translate() */
int translate_line( char *input)
{
char output[MAXLINELEN * 2];
char token[MAXLINELEN];
char *c, *cprev;
static char lastchar='\n';
int redo;
inp = eatwhitespace( input);
/*
* Process lines that are inside of @menu blocks:
*
* - After each MENU token the global inside_menu variable is 1
* - The menu_entry_maybe() routine returns 1 if the current string
* is a menu entry and delivers the entry itself. We bypass the
* normal processing here!
* - Before the first appearing menu entry we must put the parameter
* list environment start tag.
* - We close this environment as soon as a non menu entry appears and
* open it again if necessary. If a non menu entry seems to belong to
* a description started in the line before we don't close the entry
* environment. We use a very rough measure saying a line with more
* than 25 leading blanks belongs to a description from above. Tab
* character are treated as 8 spaces.
*/
if ( inside_menu ){
was_entry = is_entry;
is_entry = menu_entry_maybe( inp, output);
}/* endif */
if ( inside_menu && is_entry && !was_entry ){
char tmp[MAXLINELEN*2];
strcpy( tmp, output);
sprintf( output, "%s%s%c",
":parml tsize=40 break=none compact.\n", tmp, '\0');
} else if ( inside_menu && !is_entry && was_entry ){
int i = 0, j = 0;
while ( input[i] && isspace( (int)input[i]) ){
j += input[i] == '\t' ? 8 : 1;
i++;
}/* while */
if ( j > 25 ){
char * inptr = input + i - 1;
is_entry = YES;
translate_string( inptr);
remove_texicmds( inptr);
strcpy( output, inptr);
/* fprintf( stdout, "%s\n", outptr); */
} else
fprintf( stdout, "%s\n", ":eparml.");
/* endif */
}/* if */
if ( !is_entry ){
strcat(input,"\n"); /* Add end-of-line (has been removed) */
do {
inp = input;
*output = '\0';
vistext = NO; /* No visibile text displayed yet */
gettoken( NULL, NULL); /* tell tab expander of newline */
while ( *inp != '\0' ) {
inp = gettoken( inp, token);
inp = interpret( token, output);
if ( inp == NULL )
return ERROR;
}/* while */
rtrim( output); /* strip trailing whitespaces/returns */
if( *lastnode && vistext && *output ) {
/* @node, but no title */
sprintf( token, "@chapter %s", lastnode);
translate_line( token); /* Create artificial title */
redo = 1; /* Line conversion might have changed... */
} else
redo = 0;
/* endif */
} while ( redo );
}/* if */
/*
* output, stripping surplus newlines when possible.
*/
if( newpara>=0 && *output && !nopara ) {
/* if not done, add paragraph tag */
if( ilevel == newpara )
(void) fputs( displaylevel?
"\n":
(newpara==0 || firstitem==0)?
cmds->dfltpara:
(what[ilevel]==TABLE || what[ilevel]==APPLY)?
cmds->dflttpara:
cmds->dfltipara,
stdout);
/* endif */
newpara = -1;
firstitem = 0; /* more paragraphs may follow */
}/* if */
if(*output) {
nopara = 0; /* maybe paragraph before next line */
strcat( output, "\n"); /* one definitive return... */
}/* if */
cprev = &lastchar; /* character at end of previous output buffer */
for( c = output; *c != '\0'; cprev = c, ++c )
if (*c != '\n' || *cprev != '\n')
putc( *c, stdout);
/* endif */
/* endfor */
lastchar = *cprev;
return 0; /* no error */
}/* translate_line() */
/*
* PUSH - macro to push pointer to table entry onto command stack
* and current font onto font stack
*/
#define MAXDEPTH 20
#define PUSH(tptr) \
{ if (++stackptr >= MAXDEPTH) { \
errormsg("stack overflow - commands nested too deeply", ""); \
return NULL; \
} \
stack[stackptr] = (tptr); \
(void) strcpy(fontstack[++fontptr], curfont); \
if (*(tptr)->font != '\0' && !discarding) \
(void) strcpy(curfont, (tptr)->font); \
}
#define OUTPUT(tptr)\
{ (void) strcat((argmode>=0 && argmode<MAXARG)? \
macroarg[argmode]: \
outstring, \
(tptr)); \
}
struct tablerecd brackets = {"{","}","","","",INPARA};
/*
* interpret - interprets and concatenates interpreted token onto outstring
*/
char * interpret( char *token, char *outstring)
{
static struct tablerecd *stack[MAXDEPTH];
static int stackptr = 0; /* zeroth element is not used */
static int discarding = NO;
static int discardlevel = MAXDEPTH;
static int fontptr;
static char fontstack[MAXDEPTH][FONTSTRSIZE];
static char curfont[FONTSTRSIZE];
static char defaultfont[] = ":font facename=default size=0x0.";
static int init = NO;
struct tablerecd *tptr,*tptr2;
char *s, *cp, tempstr[MAXLINELEN],itemtag[MAXLINELEN];
int level,i,retry;
FILE *fp; /* for @include files */
int process( FILE*, char *); /* for @include files */
if (init == NO) {
(void) strcpy( fontstack[0], defaultfont);
(void) strcpy( curfont, defaultfont);
fontptr = 0;
init = YES;
}/* if */
if (*token == '@') { /* attempt to look up texinfo cmds */
if ( !(tptr = lookup(token)) ) { /* not found in present form? */
cp = token + strlen( token) - 1;
if (*cp == '{') {
*cp = '\0'; /* try removing the trailing "{" */
if ( (tptr = lookup(token)) != (struct tablerecd*)0 ) {
/* valid without the "{"? */
inp--; /* unget "{" char */
} else
*cp = '{'; /* no use, replace for error message */ /* endif */
}/* if */
}/* if */
} else
tptr = (*token == '{')? &brackets : NULL;
/* endif */
s = inp;
if ( stackptr > 0 && STREQ(token, stack[stackptr]->texend) ) {
/* have fetched closing token of current Texinfo command */
if ( STREQ( token, "@end") ) /* get second half of end command */
s = gettoken( eatwhitespace(s), tempstr);
/* endif */
do {
if ( discarding && stackptr <= discardlevel ) {
discarding = NO;
discardlevel = MAXDEPTH;
}/* if */
if ( inmacroarg && stackptr <= inmacroarg )
inmacroarg = NO;
/* endif */
retry = NO; /* no need roll back multiple @ends */
if ( STREQ( token, "@end")) {
/*
* Now this is a bit tricky. If we're waiting for an
* "@end ignore" we don't check for correct "@end" nesting
* any more (comment!). If there's an "ignore" somewhere on
* the stack we can discard anything on top of it!
*/
if( ! STREQ( &(stack[stackptr]->texstart[1]), tempstr) ){
/*
* If "ignore" is on top of the stack we *must not*
* decrement the stack pointer.
*/
if ( !STREQ( &(stack[stackptr]->texstart[1]), "ignore") ){
/*
* But if there's an "ignore" somewhere below on the
* stack we have to decrement the stack but need not
* print a warning message!
*/
if ( !ignoring ){
errormsg( "found unexpected @end ", tempstr);
errormsg( "skipped missing @end ",
stack[stackptr]->texstart+1);
}/* if */
retry = YES; /* Skip back end */
} else
break; /* break and leave the stack untouched! */
/* endif */
}/* if */
if ( stack[stackptr]->type == HEADING1 )
started = NO; /* end of titlepage - skip again */
/* endif */
}/* if */
/* End of something with arguments */
if( stack[stackptr]->type == XREF || stack[stackptr]->type == NODE
|| stack[stackptr]->type == SETCLEAR ) {
if( argmode < MAXARG )
rtrim( macroarg[argmode]);
/* endif */
i = argmode;
argmode = -1;
switch( stack[stackptr]->type ) {
case XREF:
if( !discarding && *macroarg[0] ) {
if( i >= 3 && *macroarg[3] ) {
/* reference to another file */
if ( (cp = strrchr(macroarg[3],'.'))!=(char*)0 )
*cp = '\0'; /* remove extension ".info" */
sprintf( macroarg[1], "database='%s.INF'",
macroarg[3]);
/* create "database" option for link */
} else
*macroarg[1]='\0';
sprintf( outstring+strlen( outstring),
stack[stackptr]->ipfstart,
macroarg[1],macroarg[0]);
OUTPUT( macroarg[(i>=2 && *macroarg[2])?2:0 ]);
OUTPUT( stack[stackptr]->ipfend);
}/* if */
break;
case NODE:
if( !discarding )
strcpy( lastnode, macroarg[0]);
/* endif */
break;
case SETCLEAR:
if ( *macroarg[1]=='\0' )
if ( STREQ( stack[stackptr]->texstart,"@set") )
strcpy( macroarg[1],"*");
/* endif */
/* endif */
if ( setclear( macroarg[0], macroarg[1]) < 0 )
fprintf( stderr,
"Warning! Could not @set or @setclear %s!\n",
macroarg[1]);
/* endif */
break;
}/* switch */
} else if( !discarding
&& (!inmacroarg || stack[stackptr]->type != INPARA)
&& (started || stack[stackptr]->type == PARAM) )
OUTPUT( stack[stackptr]->ipfend);
/* endif */
switch( stack[stackptr]->type ) {
case DISPLAY:
--displaylevel;
newpara = ilevel;
nopara = 1;
break;
case ITEMIZING:
--ilevel;
newpara = ilevel;
nopara = 1;
if ( !discarding && ilevel > 0 )
OUTPUT( cmds->indentend);
break;
case HEADING1:
case HEADING2:
case HEADING3:
case HEADING4:
newpara=ilevel;
nopara=1;
break;
}/* switch */
if ( --stackptr < 0 ) {
errormsg( "stack underflow", "");
return NULL;
}/* if */
/* restore previous active font */
if ( STREQ( curfont, fontstack[fontptr]) == NO ) {
(void) strcpy( curfont, fontstack[fontptr]);
if( !inmacroarg && started )
OUTPUT( curfont); /* don't exec font changes in titles */
/* endif */
}/* if */
if ( fontptr > 0 )
--fontptr;
/* endif */
} while ( stackptr
&& ((*token == '\n' && *(stack[stackptr]->texend)=='\n')
|| (retry && STREQ(stack[stackptr]->texend,"@end"))) );
if ( retry ) { /* cannot rollback @end's */
errormsg( "nesting level rollback failed", "");
return NULL; /* fatal error */
}/* if */
if ( STREQ( token, "@end") ){
if ( STREQ( tempstr, "ignore") )
ignoring = NO;
/* endif */
return ""; /* flush rest of line if any */
}/* if */
} else if ( *token == ',' && argmode>=0 ) {
if ( argmode < MAXARG )
rtrim( macroarg[argmode]);
/* endif */
if ( ++argmode < MAXARG )
*macroarg[argmode]='\0';
/* endif */
} else if ( tptr == NULL ) { /* ordinary piece of text */
if ( *token == '@' && !discarding && !ignoring ) {
char * ptr = strstr( token, "index");
if ( ptr != NULL ){
int tokenlen = (int)strlen( token);
if ( strlen( "index") + ptr-token == tokenlen ){
errormsg( "custom indices are not supported, ignoring ",
token);
return "";
}/* if */
}/* if */
errormsg( "unrecognized Texinfo command ", token);
return "";
}/* if */
if ( argmode >= 0 && argmode < MAXARG ) {
if ( *token == '\n' ) {
if( *macroarg[argmode] )
strcat( macroarg[argmode], " ");
} else if ( *token != ' ' || *macroarg[argmode] )
strcat( macroarg[argmode], token);
} else if ( !discarding
&& argmode < 0
&& (started
|| (stackptr && stack[stackptr]->type == PARAM)
|| *lastnode) ) {
OUTPUT( token);
if( !inmacroarg )
vistext = YES;
/* endif */
}/* if */
if ( *token == '\n' )
return "";
/* endif */
} else { /* start of Texinfo command */
switch ( tptr->type ) {
case ESCAPED:
if ( !discarding && started )
OUTPUT( tptr->ipfstart);
/* endif */
break;
case DISPLAY:
++displaylevel;
PUSH( tptr);
if ( !discarding && displaylevel<2 )
OUTPUT( tptr->ipfstart);
/* endif */
newpara = -1;
break;
case INDEX:
nopara = 1; /* move para tag after index entry */
case PARAM:
inmacroarg = stackptr+1; /* no font changes in parameters */
/* fall through */
case PARAGRAPH:
case CHAR: /* may be some need to distinguish these in future */
case INPARA:
case FOOTNOTE:
case TEMPLATE:
case TEMPLATE2:
case TEMPLATE3:
case TEMPLATE4:
s = eatwhitespace(s);
PUSH( tptr);
/*
* This effort is being taken to automatically generate
* main index entries for all TEMPLATE*'s. This could also
* be the base for generating index'es like in Texinfo.
*/
if ( tptr->type == TEMPLATE
|| tptr->type == TEMPLATE2
|| tptr->type == TEMPLATE3
|| tptr->type == TEMPLATE4 ){
char tmpbuf[MAXLINELEN*2];
char fubpmt[MAXLINELEN];
char *pmt = fubpmt;
strcpy( fubpmt, inp);
switch ( tptr->type ){
case TEMPLATE: /* All templates with "category" and "type"
before name need two words removed. */
pmt = eat_first_word( pmt);
/* fallthrough */
case TEMPLATE2: /* TEMPLATE2 only need one word removed */
case TEMPLATE3: /* TEMPLATE3 are normally like TEMPLATE,
only they have only "category" before
name */
pmt = eat_first_word( pmt);
case TEMPLATE4: /* TEMPLATE4 has no "type" before name so
there's no need to preprocess the string */
/*
* Now tmpbuf gets the whole entry for the main index.
* pmt points to the name of the entry. This could be
* used to generate a Texinfo-like index. For this we
* would have to distinguish between all the different
* kinds of index'es (Type, Variables, Functions...) and
* append lines to dynamically allocated buffers. These
* buffered could then get put out to stdout when the
* @printindex command appears in the Texinfo source
* file.
*/
remove_texicmds( pmt);
sprintf( tmpbuf, "%s%s", ":i1.", pmt);
OUTPUT( tmpbuf);
break;
default: break;
}/* switch */
}/* if */
if ( !discarding && !(tptr->type==INPARA && inmacroarg) ) {
if ( started || tptr->type == PARAM ) {
if ( tptr->type == TEMPLATE
|| tptr->type == TEMPLATE2
|| tptr->type == TEMPLATE3
|| tptr->type == TEMPLATE4 ) {
PUSH( &tempframe);
OUTPUT( temp1stword.ipfstart);
}/* if */
if ( tptr->type == FOOTNOTE ){
char tmpbuf[60];
sprintf( tmpbuf, "%s%i reftype=fn.(%i):elink.:fn id=%i.",
tptr->ipfstart, footnote_nr, footnote_nr, footnote_nr);
OUTPUT( tmpbuf);
footnote_nr++;
} else
OUTPUT( tptr->ipfstart);
/* endif */
if ( tptr->type == TEMPLATE || tptr->type == TEMPLATE3 ) {
if ( *s == '{' ) {
tptr2 = &temp1stbrac;
s++;
} else
tptr2 = &temp1stword;
PUSH(tptr2);
} else if ( tptr->type == TEMPLATE2
|| tptr->type == TEMPLATE4 )
OUTPUT( temp1stword.ipfend);
/* endif */
}/* if */
}/* if */
break;
case HEADING1:
case HEADING2:
case HEADING3:
case HEADING4:
inmacroarg = stackptr+1; /* no font changes in headlines */
s = eatwhitespace( s);
PUSH( tptr);
if ( !discarding ) {
level = tptr->type - HEADING1 + 1;
if( level - lastlevel > 1 )
while ( level - lastlevel > 1 ) level--;
/* Correct missing heading levels */
else
lastlevel = level;
/* endif */
if ( *lastnode )
sprintf(tempstr," id=\'%s\'",lastnode);
else
*tempstr = '\0';
/* endif */
*lastnode = '\0'; /* @node command has been processed */
if ( !started )
started = YES; /* stop skipping over header */
else
OUTPUT( cmds->dfltpara);
/* endif */
/* para, so panel is never empty */
newpara = -1; /* swallow newlines at end of panel */
sprintf( outstring+strlen( outstring),
cmds->heading, level, tempstr);
OUTPUT( tptr->ipfstart);
}/* if */
break;
case MENU:
inside_menu = YES;
is_entry = 0;
PUSH( tptr);
break;
case VERBATIM: /* This was one of my weird @ipfline{} commands.
* We kill the "}" at the end and put it into the
* output string without further processing.
*/
{
char * tmp = strrchr( s, (int)*tptr->texend);
s = eatwhitespace( s);
if ( tmp == NULL )
fprintf( stderr, "Missing \"}\" for @ipfline command!"
" Ignored...\n");
else
*tmp = '\0';
/* endif */
OUTPUT( s);
discarding = YES;
break;
}/* case VERBATIM */
case DISCARD:
PUSH( tptr);
if ( !discarding ) {
OUTPUT( tptr->ipfstart);
discarding = YES;
discardlevel = stackptr;
}/* if */
if ( STREQ( tptr->texstart, "@ignore") )
ignoring = YES;
/* endif */
break;
case BYE: /* Document ends before end of file is reached. Simply
* leaving the program won't do, the ":euserdoc." gets
* entered in main(). Jump back to it! */
longjmp( cleanup_point, 1);
break; /* keep the compiler happy */
case ITEMIZING:
if ( !discarding ) {
OUTPUT( tptr->ipfstart);
if ( ilevel > 0 )
OUTPUT( cmds->indentstart);
}/* if */
PUSH( tptr);
++ilevel;
s = itemize( s, token);
newpara=-1;
break;
case ITEM:
PUSH( tptr);
if ( !discarding ) {
OUTPUT( tptr->ipfstart);
/* set up, parse and interpret item tag */
s = doitem( eatwhitespace( s), itemtag,
STREQ( token, "@itemx"));
cp = itemtag;
while ( *cp != '\0' ) {
cp = gettoken( cp, tempstr);
(void) interpret( tempstr, outstring);
}/* while */
if(what[ilevel]!=ITEMIZE && what[ilevel]!=ENUMERATE)
nopara=1; /* delay until all item's are through */
newpara=ilevel; /* add paragraph before description */
firstitem=1; /* first paragraph in item follows */
}/* if */
break;
case END:
s = gettoken( eatwhitespace( s), tempstr);
if ( !strcmp( s, "menu") )
inside_menu = NO;
/* endif */
if ( !discarding && !ignoring )
errormsg( "unexpected @end found for Texinfo cmd @", tempstr);
/* endif */
break;
case INCLUDE:
s = eatwhitespace( s);
for ( cp = tempstr;
strchr(" \t\n", (int)*s) == NULL;
*cp++ = *s++ )
/* nix */;
*cp = '\0';
if ( !discarding ) {
cp = strrchr( tempstr,'.');
/* try to find "extension" dot */
/* The following statement (relying strongly on short-circuit evaluation)
attempts to open first the specified filename, and then, all three
.tex, .texi and .texinfo filenames, if one of these extensions was
specified. This allows some files with original unix names to be
compiled on FAT partitions with no modification. */
if ( (fp = fopen(tempstr, "r")) == NULL
/* try original file name/not found: */
&& (!cp /* if extension is ".tex[i[nfo]]... */
|| (!STREQ(cp,".tex") && !STREQ(cp,".texi")
&& !STREQ(cp,".texinfo"))
|| (!(strcpy(cp,".tex"),fp = fopen(tempstr,"r"))
&& !(fp = fopen(strcat(tempstr,"i"),"r"))
&& !(fp = fopen(strcat(tempstr,"nfo"),"r")) )) )
/* ...try all three extensions */
errormsg( "can't open included file ", tempstr);
else {
(void) process( fp, tempstr);
(void) fclose( fp);
}/* if */
}/* if */
break;
case SETCLEAR:
PUSH( tptr);
if( !discarding ) {
s = eatwhitespace( gettoken( eatwhitespace( s), macroarg[0]));
/* get tag name */
argmode = 1; /* prepare for reading value */
*macroarg[argmode]='\0';
}/* if */
break;
case VALUE:
PUSH( tptr);
if( !discarding ) {
s = gettoken( eatwhitespace( s), tempstr);
cp = value( tempstr);
if( cp!=NULL )
OUTPUT( cp);
/* endif */
}/* if */
break;
case CONDITION:
PUSH( tptr);
if( !discarding )
s = gettoken( eatwhitespace( s), tempstr);
/* endif */
tempstr[MAXTAGSIZE-1]='\0';
cp = value( tempstr);
if( (cp != NULL && *cp) ^ STREQ( token, "@ifclear") ) {
OUTPUT( tptr->ipfstart);
} else {
discarding = YES;
discardlevel = stackptr;
}/* if */
break;
case NODE:
case XREF:
PUSH( tptr);
if( !discarding ) {
s = eatwhitespace( s);
argmode=0;
*macroarg[argmode]='\0';
}/* if */
break;
case COMMENT: /* avoid any mis-interpretation */
return ""; /* leave immediately */
default:
/* can't happen */
errormsg( "ack ptui, what was that thing? ", token);
}/* switch */
}/* if */
return s;
}/* interpret() */
/*
* strpbrk_like - returns pointer to the leftmost occurrence in str of any
* character in set, else pointer to terminating null.
*/
char * strpbrk_like( char *str, char *set)
{
static int inited_set = 0;
static char set_vec[256] = { 0 };
if ( !inited_set ) { /* we *know* it'll be the same every time... */
while ( *set )
set_vec[(unsigned char)*set++] = 1;
set_vec[0] = 1;
inited_set = 1;
}/* if */
while ( set_vec[(int)*str] == 0 )
++str;
/* endwhile */
return str;
}/* strpbrk_like() */
/*
* gettoken - fetch next token from input buffer. leave the input pointer
* pointing to char after token. may need to be modified when
* new Texinfo commands are added which use different token boundaries.
*
* will handle case where fgets() has split a long line in the middle
* of a token, but the token will appear to have been divided by a blank
*
* will expand tab characters to "stupid" 8-character-tabs, as they
* would be created by an editor automatically "tabbing" lines.
*/
char * gettoken( char *s, char *token)
{
static char endchars[] = " \n\t@{}:.*,";
char *q, *t;
static int xpos;
if( s==NULL ) { /* Init new line */
xpos = 0;
return NULL;
}/* if */
q = s;
s = strpbrk_like( q, endchars);
if ( s != q ) {
switch ( *s ) {
case ' ':
case '\n':
case '\t':
case '@':
case '}':
case ':':
case '.':
case '*':
case '\0':
case ',':
--s;
break;
case '{':
break;
}/* switch */
} else { /* *s == *q */
switch ( *s ) {
case ' ':
case '\n':
case '\t':
case '{':
case ':':
case '.':
case '*':
case '\0':
break;
case '}':
if ( *(s+1) == '{' ) /* footnotes with daggers and dbl daggers!! */
++s;
break;
case '@':
s = strpbrk_like( q + 1, endchars);
/* handles 2 char @ tokens: @{ @} @@ @: @. @* */
if ( strchr("{}@:.*", (int)*s) == NULL
|| (s > q+1 && (*s =='}' || *s == '@')) )
--s;
break;
}/* switch */
}/* if */
for ( t = token; q <= s; ++q, ++t, ++xpos ) {
switch ( *q ) {
case ':':
strcpy( t, "&colon.");
t+=6;
break;
case '&':
strcpy( t, "&.");
t+=4;
break;
case '.':
strcpy( t, "&per.");
t+=4;
break;
case '\0':
*t = ' ';
break;
case '\t':
do {
*(t++)=' ';
} while ( (++xpos)%8 ); /* file with blanks up to next 8-tab */
t--; xpos--; /* last inc will be done by loop */
break;
case '\'':
if( argmode >= 0 ) {
*t = '_'; /* Change ' to _ in xref's */
break;
}/* if */
default :
*t = *q;
break;
}/* switch */
}/* for */
*t = 0;
return ++s;
}/* gettoken() */