home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / texipf21.zip / texi2ipf / translat.c < prev   
C/C++ Source or Header  |  1997-02-15  |  35KB  |  997 lines

  1. /* 
  2.  * translate.c - main guts of texi2ipf
  3.  *
  4.  * texi2roff history:
  5.  *             Release 1.0a    August 1988
  6.  *             Release 2.0     January 1990
  7.  *
  8.  * Copyright 1988, 1989, 1990  Beverly A.Erlebacher
  9.  * erlebach@cs.toronto.edu    ...uunet!utai!erlebach
  10.  *
  11.  * texi2ipf history:
  12.  *             Release 1.0     February 1993
  13.  *
  14.  * Modified by Marcus Gröber, Fido 2:2402/61.1
  15.  *
  16.  * Modified by Martin "Herbert" Dietze, Email herbert@wiloyee.shnet.org
  17.  *
  18.  */
  19.  
  20. /*
  21.  * History:
  22.  *
  23.  * $Log: translat.c,v $
  24.  * Revision 1.10  1997/02/06 12:45:18  herbert
  25.  * - Added documentation in Texinfo format.
  26.  * - Minor bug fixes.
  27.  *
  28.  * Revision 1.9  1997/02/04 13:05:43  herbert
  29.  * Menu descriptions of more than one lines are now converted correctly if they
  30.  * are indented by more than 25 whitespaces (1 tab == 8 spaces).
  31.  *
  32.  * Revision 1.8  1997/01/16 13:41:33  herbert
  33.  * Put stuff from translat.c to new file toolz.c.
  34.  * Fixed an error in the remove_texicmd() routine (correct handling of "@@",
  35.  * "@{" and "@}" tags).
  36.  *
  37.  * Revision 1.7  1997/01/16 10:45:14  herbert
  38.  * Added routine remove_texicmds() for deformatting automatically generated
  39.  * index entries.
  40.  *
  41.  * Revision 1.6  1997/01/15 13:34:19  herbert
  42.  * - Fixed the index entry generating for some @def* commands.
  43.  *
  44.  * Revision 1.5  1997/01/15 12:18:19  herbert
  45.  * - Fixed a bug in eat_first_word() that made the program crash sometimes.
  46.  * - "@ignore" environments will now be handled like real comments.
  47.  *
  48.  * Revision 1.4  1996/12/17 15:14:23  herbert
  49.  * Only some cosmetic changes. The code looks still rather ugly to me :-)
  50.  *
  51.  * Revision 1.3  1996/12/17 14:10:01  herbert
  52.  * Added support for pseudo-Texinfo-commands: @ifhtml (ignored) and @ipfline{}
  53.  * (my invention) for ptuting IPF code into the Texinfo source.
  54.  * Added @macro command to table.h, will be ignored.
  55.  *
  56.  * Revision 1.2  1996/12/04 12:02:20  herbert
  57.  * Added new target "commit" to makefile for easier generation of updated
  58.  * packages.
  59.  * Changed error messages for @...index commands to "ignore" message.
  60.  *
  61.  * Revision 1.1.1.1  1996/12/02 12:10:01  herbert
  62.  * Texi2IPF 1.0
  63.  *
  64.  */
  65.  
  66. #include "texi2ipf.h"
  67. #include <stdio.h>
  68. #include <string.h>
  69. #include <ctype.h>
  70. #include <setjmp.h>
  71.  
  72. static char * id =
  73. "@(#)$Id: translat.c,v 1.10 1997/02/06 12:45:18 herbert Exp $";
  74.  
  75.  
  76. #define FONTSTRSIZE 40
  77.  
  78. extern jmp_buf cleanup_point;
  79.  
  80. extern int what[MAXILEVEL];
  81. int    displaylevel = 0;       /* nesting level of 'display' text */
  82. int    inmacroarg = 0;         /* protect roff macro args flag */
  83. int    ilevel = 0;             /* nesting level of itemized lists */
  84. int    linecount;
  85. int    newpara=-1,nopara=0,firstitem=0;
  86. int    started = 0;            /* text has not yet started */
  87. int    lastlevel = 1;          /* level of last heading */
  88. int    vistext;                /* set if visible text encountered */
  89.  
  90. char   *filename;
  91. char   *inp;                   /* pointer into input buffer */
  92.  
  93. char lastnode[MAXLINELEN]="\0";    /* last node id */
  94. char macroarg[MAXARG][MAXLINELEN]; /* buffor for xref arguments */
  95. int argmode=-1;
  96. int inside_menu = NO;          /* We're inside of a menu block. */
  97. int is_entry = 0;
  98. int was_entry = 0;
  99. int ignoring = NO;
  100.  
  101. int footnote_nr = 1;          /* Actual footnote Number */
  102.  
  103. struct tablerecd * lookup( char *);
  104. char * itemize( char *, char *);
  105. char * doitem( char *, char *, int);
  106. char * value( char *);
  107. int setclear( char *, char *);
  108.  
  109. /* forward references */
  110. void errormsg( char *, char *);
  111. char * gettoken( char *, char *);
  112. char * eatwhitespace( char *);
  113. char * rtrim( char *);
  114. char * interpret( char *, char *);
  115. int translate_line( char *);
  116. char * eat_first_word( char *);
  117. int menu_entry_maybe( char *, char *);
  118. void remove_texicmds( char *);
  119. void translate_string( char*);
  120.  
  121. extern struct tablerecd tempframe, temp1stbrac, temp1stword;
  122.  
  123. /*
  124.  * translate - translate one Texinfo file
  125.  */
  126.  
  127. int translate( FILE *in, char *inname)
  128. {
  129.     char          input[MAXLINELEN];
  130.     int           savlinct;
  131.     char          *savfilnam;
  132.  
  133.     /*
  134.      * save global variables linecount and filename in case this is a
  135.      * recursive call to translate an @include file.  these variables
  136.      * are used by errormsg() which is called from many places.
  137.      */
  138.     savfilnam = filename;
  139.     savlinct = linecount;
  140.  
  141.     filename = inname;
  142.     linecount = 0;
  143.  
  144.     /*
  145.      * if fgets() truncates a line in the middle of a token, a blank will
  146.      * appear in the word containing the MAXLINELENth char in the absurdly
  147.      * long line. this is handled by gettoken()
  148.      */
  149.  
  150.     while ( fgets(input, MAXLINELEN, in) != NULL ) {
  151.         ++linecount;
  152.         rtrim(input); 
  153.         if ( *input == '\0' && started )
  154.             newpara = ilevel;            /* remeber paragraph */
  155.         else
  156.             if( translate_line (input) ) 
  157.                 return ERROR;
  158.             /* endif */
  159.         /* endif */
  160.     }/* while */
  161.     /* restore the globals */
  162.     filename = savfilnam;
  163.     linecount = savlinct;
  164.     return 0;
  165. }/* translate() */
  166.  
  167. int translate_line( char *input)
  168. {
  169.     char          output[MAXLINELEN * 2];
  170.     char          token[MAXLINELEN];
  171.     char          *c, *cprev;
  172.     static char   lastchar='\n';
  173.     int           redo;
  174.  
  175.     inp = eatwhitespace( input);
  176.  
  177.     /*
  178.      * Process lines that are inside of @menu blocks:
  179.      *
  180.      * - After each MENU token the global inside_menu variable is 1
  181.      * - The menu_entry_maybe() routine returns 1 if the current string
  182.      *   is a menu entry and delivers the entry itself. We bypass the
  183.      *   normal processing here!
  184.      * - Before the first appearing menu entry we must put the parameter
  185.      *   list environment start tag.
  186.      * - We close this environment as soon as a non menu entry appears and
  187.      *   open it again if necessary. If a non menu entry seems to belong to
  188.      *   a description started in the line before we don't close the entry
  189.      *   environment. We use a very rough measure saying a line with more
  190.      *   than 25 leading blanks belongs to a description from above. Tab
  191.      *   character are treated as 8 spaces.
  192.      */
  193.     if ( inside_menu ){
  194.         was_entry = is_entry;
  195.         is_entry = menu_entry_maybe( inp, output);
  196.     }/* endif */
  197.     
  198.     if ( inside_menu && is_entry && !was_entry ){
  199.         char tmp[MAXLINELEN*2];
  200.         strcpy( tmp, output);
  201.         sprintf( output, "%s%s%c", 
  202.                  ":parml tsize=40 break=none compact.\n", tmp, '\0');
  203.     } else if ( inside_menu && !is_entry && was_entry ){
  204.     int i = 0, j = 0;
  205.     while ( input[i] && isspace( (int)input[i]) ){
  206.         j += input[i] == '\t' ? 8 : 1; 
  207.         i++;
  208.     }/* while */
  209.     if ( j > 25 ){
  210.         char * inptr = input + i - 1;
  211.         is_entry = YES;
  212.         translate_string( inptr);
  213.         remove_texicmds( inptr);
  214.         strcpy( output, inptr);
  215.         /* fprintf( stdout, "%s\n", outptr); */
  216.     } else
  217.             fprintf( stdout, "%s\n", ":eparml.");
  218.         /* endif */
  219.     }/* if */
  220.     if ( !is_entry ){
  221.  
  222.         strcat(input,"\n");        /* Add end-of-line (has been removed) */
  223.  
  224.         do {
  225.             inp = input;
  226.             *output = '\0';
  227.             vistext = NO;               /* No visibile text displayed yet */
  228.             
  229.             gettoken( NULL, NULL);        /* tell tab expander of newline */
  230.             while ( *inp != '\0' ) {
  231.                 inp = gettoken( inp, token);
  232.                 inp = interpret( token, output); 
  233.                 if ( inp == NULL )
  234.                     return ERROR;
  235.             }/* while */
  236.         
  237.             rtrim( output);          /* strip trailing whitespaces/returns */
  238.  
  239.             if( *lastnode && vistext && *output ) {
  240.                 /* @node, but no title */
  241.                 sprintf( token, "@chapter %s", lastnode);
  242.                 translate_line( token);    /* Create artificial title */
  243.                 redo = 1;         /* Line conversion might have changed... */
  244.             } else
  245.                 redo = 0;
  246.             /* endif */
  247.             
  248.         } while ( redo );
  249.     }/* if */
  250.     
  251. /*
  252.  * output, stripping surplus newlines when possible.
  253.  */ 
  254.     if( newpara>=0 && *output && !nopara ) {
  255.         /* if not done, add paragraph tag */
  256.         if( ilevel == newpara )
  257.             (void) fputs( displaylevel?
  258.                          "\n":
  259.                          (newpara==0 || firstitem==0)?
  260.                          cmds->dfltpara:
  261.                          (what[ilevel]==TABLE || what[ilevel]==APPLY)?
  262.                          cmds->dflttpara:
  263.                          cmds->dfltipara,
  264.                          stdout);
  265.         /* endif */
  266.         newpara = -1;
  267.         firstitem = 0;               /* more paragraphs may follow */
  268.     }/* if */
  269.  
  270.     if(*output) {
  271.         nopara = 0;                    /* maybe paragraph before next line */
  272.         strcat( output, "\n");           /* one definitive return... */
  273.     }/* if */
  274.  
  275.     cprev = &lastchar;   /* character at end of previous output buffer */
  276.     for( c = output; *c != '\0'; cprev = c, ++c ) 
  277.         if (*c != '\n' || *cprev != '\n') 
  278.             putc( *c, stdout);
  279.         /* endif */
  280.     /* endfor */
  281.         
  282.     lastchar = *cprev;
  283.     return 0;                        /* no error */
  284. }/* translate_line() */
  285.  
  286. /*
  287.  * PUSH - macro to push pointer to table entry onto command stack
  288.  *       and current font onto font stack
  289.  */
  290.  
  291. #define MAXDEPTH    20
  292.  
  293. #define PUSH(tptr)                                                     \
  294. {   if (++stackptr >= MAXDEPTH) {                                      \
  295.         errormsg("stack overflow - commands nested too deeply", "");   \
  296.         return NULL;                                                   \
  297.     }                                                                  \
  298.     stack[stackptr] = (tptr);                                          \
  299.     (void) strcpy(fontstack[++fontptr], curfont);                      \
  300.     if (*(tptr)->font != '\0' && !discarding)                          \
  301.     (void) strcpy(curfont, (tptr)->font);                              \
  302. }
  303.  
  304. #define OUTPUT(tptr)\
  305. {   (void) strcat((argmode>=0 && argmode<MAXARG)? \
  306.                   macroarg[argmode]:              \
  307.                   outstring,                      \
  308.                   (tptr));                        \
  309. }
  310.  
  311. struct tablerecd brackets = {"{","}","","","",INPARA};
  312.  
  313.  
  314. /*
  315.  * interpret - interprets and concatenates interpreted token onto outstring
  316.  */
  317.  
  318. char * interpret( char *token, char *outstring)
  319. {
  320.     static struct tablerecd *stack[MAXDEPTH];
  321.     static int    stackptr = 0; /* zeroth element is not used */
  322.     static int    discarding = NO;
  323.     static int    discardlevel = MAXDEPTH;
  324.     static int    fontptr;
  325.     static char   fontstack[MAXDEPTH][FONTSTRSIZE];
  326.     static char   curfont[FONTSTRSIZE];
  327.     static char   defaultfont[] = ":font facename=default size=0x0.";
  328.     static int    init = NO;
  329.     struct tablerecd *tptr,*tptr2;
  330.     char           *s, *cp, tempstr[MAXLINELEN],itemtag[MAXLINELEN];
  331.     int            level,i,retry;
  332.     FILE           *fp;                      /* for @include files */
  333.     int     process( FILE*, char *);         /* for @include files */
  334.     if (init == NO) {
  335.         (void) strcpy( fontstack[0], defaultfont);
  336.         (void) strcpy( curfont, defaultfont);
  337.         fontptr = 0;
  338.         init = YES;
  339.     }/* if */
  340.  
  341.     if (*token == '@') {                /* attempt to look up texinfo cmds */
  342.         if ( !(tptr = lookup(token)) ) {      /* not found in present form? */
  343.             cp = token + strlen( token) - 1;
  344.             if (*cp == '{') {
  345.                 *cp = '\0';               /* try removing the trailing "{" */
  346.                 if ( (tptr = lookup(token)) != (struct tablerecd*)0 ) {
  347.                     /* valid without the "{"? */
  348.                     inp--;                 /* unget "{" char */
  349.                 } else 
  350.                     *cp = '{';        /* no use, replace for error message */                  /* endif */
  351.             }/* if */
  352.         }/* if */
  353.     } else 
  354.         tptr = (*token == '{')? &brackets : NULL;
  355.     /* endif */
  356.  
  357.     s = inp;
  358.     if ( stackptr > 0 && STREQ(token, stack[stackptr]->texend) ) {
  359.         /* have fetched closing token of current Texinfo command */
  360.         if ( STREQ( token, "@end") )    /* get second half of end command */
  361.             s = gettoken( eatwhitespace(s), tempstr);
  362.     /* endif */
  363.         do {
  364.             if ( discarding && stackptr <= discardlevel ) {
  365.                 discarding = NO;
  366.                 discardlevel = MAXDEPTH;
  367.             }/* if */
  368.             if ( inmacroarg && stackptr <= inmacroarg ) 
  369.                 inmacroarg = NO;
  370.             /* endif */
  371.             retry = NO;               /* no need roll back multiple @ends */
  372.             if ( STREQ( token, "@end")) {
  373.         /*
  374.          * Now this is a bit tricky. If we're waiting for an
  375.          * "@end ignore" we don't check for correct "@end" nesting
  376.          * any more (comment!). If there's an "ignore" somewhere on
  377.          * the stack we can discard anything on top of it!
  378.          */
  379.                 if( ! STREQ( &(stack[stackptr]->texstart[1]), tempstr) ){
  380.             /*
  381.              * If "ignore" is on top of the stack we *must not* 
  382.              * decrement the stack pointer.
  383.              */
  384.             if ( !STREQ( &(stack[stackptr]->texstart[1]), "ignore") ){
  385.             /*
  386.              * But if there's an "ignore" somewhere below on the
  387.              * stack we have to decrement the stack but need not
  388.              * print a warning message!
  389.              */
  390.                         if ( !ignoring ){
  391.                             errormsg( "found unexpected @end ", tempstr);
  392.                             errormsg( "skipped missing @end ",
  393.                                       stack[stackptr]->texstart+1);
  394.             }/* if */
  395.                         retry = YES;             /* Skip back end */
  396.             } else 
  397.             break; /* break and leave the stack untouched! */
  398.             /* endif */
  399.                 }/* if */
  400.                 if ( stack[stackptr]->type == HEADING1 )
  401.                     started = NO;         /* end of titlepage - skip again */
  402.                 /* endif */
  403.             }/* if */
  404.  
  405.             /* End of something with arguments */ 
  406.             if( stack[stackptr]->type == XREF || stack[stackptr]->type == NODE
  407.                || stack[stackptr]->type == SETCLEAR ) {
  408.                 if( argmode < MAXARG )
  409.                     rtrim( macroarg[argmode]);
  410.                 /* endif */
  411.                 i = argmode;
  412.                 argmode = -1;
  413.                 switch( stack[stackptr]->type ) {
  414.                 case XREF:
  415.                     if( !discarding && *macroarg[0] ) {
  416.                         if( i >= 3 && *macroarg[3] ) {
  417.                             /* reference to another file */
  418.                             if ( (cp = strrchr(macroarg[3],'.'))!=(char*)0 )
  419.                                 *cp = '\0';    /* remove extension ".info" */
  420.                             sprintf( macroarg[1], "database='%s.INF'",
  421.                                      macroarg[3]);
  422.                             /* create "database" option for link */
  423.                         } else
  424.                             *macroarg[1]='\0';
  425.                         sprintf( outstring+strlen( outstring),
  426.                                  stack[stackptr]->ipfstart,
  427.                                  macroarg[1],macroarg[0]);
  428.                         OUTPUT( macroarg[(i>=2 && *macroarg[2])?2:0 ]);
  429.                         OUTPUT( stack[stackptr]->ipfend);
  430.                     }/* if */
  431.                     break;
  432.  
  433.                 case NODE:
  434.                     if( !discarding ) 
  435.             strcpy( lastnode, macroarg[0]);
  436.             /* endif */
  437.                     break;
  438.  
  439.                 case SETCLEAR:
  440.                     if ( *macroarg[1]=='\0' ) 
  441.                         if ( STREQ( stack[stackptr]->texstart,"@set") ) 
  442.                             strcpy( macroarg[1],"*");
  443.                         /* endif */
  444.                     /* endif */
  445.                     if ( setclear( macroarg[0], macroarg[1]) < 0 )
  446.             fprintf( stderr, 
  447.                  "Warning! Could not @set or @setclear %s!\n",
  448.                  macroarg[1]);
  449.             /* endif */
  450.                     break;
  451.                 }/* switch */
  452.             } else if( !discarding 
  453.                     && (!inmacroarg || stack[stackptr]->type != INPARA)
  454.                     && (started || stack[stackptr]->type == PARAM) )
  455.                 OUTPUT( stack[stackptr]->ipfend);
  456.             /* endif */
  457.  
  458.             switch( stack[stackptr]->type ) {
  459.             case DISPLAY:
  460.                 --displaylevel;
  461.                 newpara = ilevel;
  462.                 nopara = 1;
  463.                 break;
  464.             case ITEMIZING:
  465.                 --ilevel;
  466.                 newpara = ilevel;
  467.                 nopara = 1;
  468.                 if ( !discarding && ilevel > 0 )
  469.                     OUTPUT( cmds->indentend);
  470.                 break;
  471.             case HEADING1:
  472.             case HEADING2:
  473.             case HEADING3:
  474.             case HEADING4:
  475.                 newpara=ilevel;
  476.                 nopara=1;
  477.                 break;
  478.             }/* switch */
  479.  
  480.             if ( --stackptr < 0 ) {
  481.                 errormsg( "stack underflow", "");
  482.                 return NULL;
  483.             }/* if */
  484.             /* restore previous active font */
  485.             if ( STREQ( curfont, fontstack[fontptr]) == NO ) {
  486.                 (void) strcpy( curfont, fontstack[fontptr]);
  487.                 if( !inmacroarg && started )
  488.                     OUTPUT( curfont);  /* don't exec font changes in titles */
  489.                 /* endif */
  490.             }/* if */
  491.             if ( fontptr > 0 )
  492.                 --fontptr;
  493.             /* endif */
  494.         } while ( stackptr 
  495.                  && ((*token == '\n' && *(stack[stackptr]->texend)=='\n')
  496.                      || (retry && STREQ(stack[stackptr]->texend,"@end"))) );
  497.         if ( retry ) {                     /* cannot rollback @end's */
  498.             errormsg( "nesting level rollback failed", "");
  499.             return NULL;                    /* fatal error */
  500.     }/* if */
  501.         if ( STREQ( token, "@end") ){
  502.             if ( STREQ( tempstr, "ignore") )
  503.                 ignoring = NO;
  504.             /* endif */
  505.             return "";                      /* flush rest of line if any */
  506.         }/* if */
  507.     } else if ( *token == ',' && argmode>=0 ) {
  508.         if ( argmode < MAXARG )
  509.             rtrim( macroarg[argmode]);
  510.         /* endif */
  511.         if ( ++argmode < MAXARG )
  512.             *macroarg[argmode]='\0';
  513.         /* endif */
  514.     } else if ( tptr == NULL ) {          /* ordinary piece of text */
  515.         if ( *token == '@' && !discarding && !ignoring ) {
  516.             char * ptr = strstr( token, "index");
  517.             if ( ptr != NULL ){
  518.                 int tokenlen = (int)strlen( token);
  519.                 if ( strlen( "index") + ptr-token == tokenlen ){
  520.                     errormsg( "custom indices are not supported, ignoring ",
  521.                               token);
  522.                     return "";
  523.                 }/* if */
  524.             }/* if */
  525.             errormsg( "unrecognized Texinfo command ", token);
  526.             return "";
  527.         }/* if */
  528.         if ( argmode >= 0 && argmode < MAXARG ) {
  529.             if ( *token == '\n' ) {
  530.                 if( *macroarg[argmode] )
  531.                     strcat( macroarg[argmode], " ");
  532.             } else if ( *token != ' ' || *macroarg[argmode] )
  533.                 strcat( macroarg[argmode], token);
  534.         } else if ( !discarding
  535.                  && argmode < 0
  536.                  && (started
  537.                      || (stackptr && stack[stackptr]->type == PARAM)
  538.                      || *lastnode) ) {
  539.             OUTPUT( token);
  540.             if( !inmacroarg ) 
  541.                 vistext = YES;
  542.             /* endif */
  543.         }/* if */
  544.         if ( *token == '\n' )
  545.             return "";
  546.         /* endif */
  547.     } else {                           /* start of Texinfo command */
  548.         switch ( tptr->type ) {
  549.         case ESCAPED:
  550.             if ( !discarding && started )
  551.                 OUTPUT( tptr->ipfstart);
  552.             /* endif */
  553.             break;
  554.  
  555.         case DISPLAY:
  556.             ++displaylevel;
  557.             PUSH( tptr);
  558.             if ( !discarding && displaylevel<2 )
  559.                 OUTPUT( tptr->ipfstart);
  560.             /* endif */
  561.             newpara = -1;
  562.             break;
  563.  
  564.         case INDEX:
  565.             nopara = 1;              /* move para tag after index entry */
  566.         case PARAM:
  567.             inmacroarg = stackptr+1; /* no font changes in parameters */
  568.             /* fall through */
  569.         case PARAGRAPH:
  570.         case CHAR:  /* may be some need to distinguish these in future */
  571.         case INPARA:
  572.         case FOOTNOTE:
  573.         case TEMPLATE:
  574.         case TEMPLATE2:
  575.         case TEMPLATE3:
  576.         case TEMPLATE4:
  577.             s = eatwhitespace(s);
  578.             PUSH( tptr);
  579.             /*
  580.              * This effort is being taken to automatically generate
  581.              * main index entries for all TEMPLATE*'s. This could also
  582.              * be the base for generating index'es like in Texinfo.
  583.              */
  584.             if ( tptr->type == TEMPLATE 
  585.                  || tptr->type == TEMPLATE2
  586.                  || tptr->type == TEMPLATE3 
  587.                  || tptr->type == TEMPLATE4 ){
  588.                 char tmpbuf[MAXLINELEN*2];
  589.                 char fubpmt[MAXLINELEN];
  590.                 char *pmt = fubpmt;
  591.                 strcpy( fubpmt, inp);
  592.                 
  593.                 switch ( tptr->type ){
  594.  
  595.                 case TEMPLATE:  /* All templates with "category" and "type"
  596.                                    before name need two words removed. */
  597.                     pmt = eat_first_word( pmt);
  598.                 /* fallthrough */    
  599.  
  600.                 case TEMPLATE2: /* TEMPLATE2 only need one word removed */
  601.                 case TEMPLATE3: /* TEMPLATE3 are normally like TEMPLATE, 
  602.                                    only they have only "category" before
  603.                                    name */
  604.                     pmt = eat_first_word( pmt); 
  605.  
  606.                 case TEMPLATE4: /* TEMPLATE4 has no "type" before name so
  607.                                    there's no need to preprocess the string */
  608.  
  609.                     /*
  610.                      * Now tmpbuf gets the whole entry for the main index.
  611.                      * pmt points to the name of the entry. This could be
  612.                      * used to generate a Texinfo-like index. For this we
  613.                      * would have to distinguish between all the different
  614.                      * kinds of index'es (Type, Variables, Functions...) and
  615.                      * append lines to dynamically allocated buffers. These
  616.                      * buffered could then get put out to stdout when the
  617.                      * @printindex command appears in the Texinfo source
  618.                      * file. 
  619.                      */
  620.                     remove_texicmds( pmt);
  621.                     sprintf( tmpbuf, "%s%s", ":i1.", pmt);
  622.                     OUTPUT( tmpbuf);
  623.                     break;
  624.  
  625.                 default: break;
  626.                 }/* switch */
  627.             }/* if */
  628.             
  629.             if ( !discarding && !(tptr->type==INPARA && inmacroarg) ) {
  630.                 if ( started || tptr->type == PARAM ) {
  631.                     if ( tptr->type == TEMPLATE
  632.                          || tptr->type == TEMPLATE2 
  633.                          || tptr->type == TEMPLATE3
  634.                          || tptr->type == TEMPLATE4 ) {
  635.                         PUSH( &tempframe);
  636.                         OUTPUT( temp1stword.ipfstart);
  637.                     }/* if */
  638.                     if ( tptr->type == FOOTNOTE ){
  639.                         char tmpbuf[60];
  640.                         sprintf( tmpbuf, "%s%i reftype=fn.(%i):elink.:fn id=%i.",
  641.                                 tptr->ipfstart, footnote_nr, footnote_nr, footnote_nr);
  642.                         OUTPUT( tmpbuf);
  643.                         footnote_nr++;
  644.                     } else
  645.                         OUTPUT( tptr->ipfstart);
  646.                     /* endif */
  647.                     if ( tptr->type == TEMPLATE || tptr->type == TEMPLATE3 ) {
  648.                         if ( *s == '{' ) {
  649.                             tptr2 = &temp1stbrac;
  650.                             s++;
  651.                         } else
  652.                             tptr2 = &temp1stword;
  653.                         PUSH(tptr2);
  654.                     } else if ( tptr->type == TEMPLATE2
  655.                                 || tptr->type == TEMPLATE4 )
  656.                         OUTPUT( temp1stword.ipfend);
  657.                     /* endif */
  658.                 }/* if */
  659.                 
  660.             }/* if */
  661.             break;
  662.  
  663.         case HEADING1:
  664.         case HEADING2:
  665.         case HEADING3:
  666.         case HEADING4:
  667.             inmacroarg = stackptr+1; /* no font changes in headlines */
  668.             s = eatwhitespace( s);
  669.             PUSH( tptr);
  670.             if ( !discarding ) {
  671.                 level = tptr->type - HEADING1 + 1;
  672.                 if( level - lastlevel > 1 )
  673.                     while ( level - lastlevel > 1 ) level--;
  674.                 /* Correct missing heading levels */
  675.                 else
  676.                     lastlevel = level;
  677.                 /* endif */
  678.                 if ( *lastnode )
  679.                     sprintf(tempstr," id=\'%s\'",lastnode);
  680.                 else
  681.                     *tempstr = '\0';
  682.                 /* endif */
  683.                 *lastnode = '\0';      /* @node command has been processed */
  684.                 if ( !started )
  685.                     started = YES;     /* stop skipping over header */
  686.                 else
  687.                     OUTPUT( cmds->dfltpara);
  688.                 /* endif */
  689.                 /* para, so panel is never empty */
  690.                 newpara = -1;          /* swallow newlines at end of panel */
  691.                 sprintf( outstring+strlen( outstring), 
  692.                         cmds->heading, level, tempstr);
  693.                 OUTPUT( tptr->ipfstart);
  694.             }/* if */
  695.             break;
  696.  
  697.         case MENU:
  698.             inside_menu = YES;
  699.             is_entry = 0;
  700.             PUSH( tptr);
  701.             break;
  702.            
  703.         case VERBATIM: /* This was one of my weird @ipfline{} commands.
  704.                         * We kill the "}" at the end and put it into the
  705.                         * output string without further processing.
  706.                         */
  707.             {
  708.                 char * tmp = strrchr( s, (int)*tptr->texend);
  709.                 s = eatwhitespace( s);
  710.                 if ( tmp == NULL ) 
  711.                     fprintf( stderr, "Missing \"}\" for @ipfline command!"
  712.                                      " Ignored...\n");
  713.                 else
  714.                     *tmp = '\0';
  715.                 /* endif */
  716.                 OUTPUT( s);
  717.                 discarding = YES;
  718.                 break;
  719.             }/* case VERBATIM */
  720.             
  721.         case DISCARD:
  722.             PUSH( tptr);
  723.             if ( !discarding ) {
  724.                 OUTPUT( tptr->ipfstart);
  725.                 discarding = YES;
  726.                 discardlevel = stackptr;
  727.             }/* if */
  728.             if ( STREQ( tptr->texstart, "@ignore") )
  729.                 ignoring = YES;
  730.             /* endif */
  731.             break;
  732.  
  733.     case BYE: /* Document ends before end of file is reached. Simply
  734.            * leaving the program won't do, the ":euserdoc." gets
  735.                * entered in main(). Jump back to it! */
  736.         longjmp( cleanup_point, 1);
  737.         break; /* keep the compiler happy */
  738.  
  739.         case ITEMIZING:
  740.             if ( !discarding ) {
  741.                 OUTPUT( tptr->ipfstart);
  742.                 if ( ilevel > 0 )
  743.                     OUTPUT( cmds->indentstart);
  744.             }/* if */
  745.             PUSH( tptr);
  746.             ++ilevel;
  747.             s = itemize( s, token);
  748.             newpara=-1;
  749.             break;
  750.  
  751.         case ITEM:
  752.             PUSH( tptr);
  753.             if ( !discarding ) {
  754.                 OUTPUT( tptr->ipfstart);
  755.                 /* set up, parse and interpret item tag */
  756.                 s = doitem( eatwhitespace( s), itemtag, 
  757.                             STREQ( token, "@itemx"));
  758.                 cp = itemtag;
  759.                 while ( *cp != '\0' ) {
  760.                     cp = gettoken( cp, tempstr);
  761.                     (void) interpret( tempstr, outstring);
  762.                 }/* while */
  763.                 if(what[ilevel]!=ITEMIZE && what[ilevel]!=ENUMERATE)
  764.                     nopara=1;          /* delay until all item's are through */
  765.                 newpara=ilevel;      /* add paragraph before description */
  766.                 firstitem=1;         /* first paragraph in item follows */
  767.             }/* if */
  768.             break;
  769.  
  770.         case END:
  771.             s = gettoken( eatwhitespace( s), tempstr);
  772.             if ( !strcmp( s, "menu") )
  773.                 inside_menu = NO;
  774.             /* endif */
  775.             if ( !discarding && !ignoring )
  776.                 errormsg( "unexpected @end found for Texinfo cmd @", tempstr);
  777.             /* endif */
  778.             break;
  779.  
  780.         case INCLUDE:
  781.             s = eatwhitespace( s);
  782.             for ( cp = tempstr; 
  783.                   strchr(" \t\n", (int)*s) == NULL; 
  784.                   *cp++ = *s++ )
  785.                 /* nix */;
  786.             *cp = '\0';
  787.             if ( !discarding ) {
  788.                 cp = strrchr( tempstr,'.');
  789.                 /* try to find "extension" dot */
  790.  
  791. /* The following statement (relying strongly on short-circuit evaluation)
  792.    attempts to open first the specified filename, and then, all three
  793.    .tex, .texi and .texinfo filenames, if one of these extensions was
  794.    specified. This allows some files with original unix names to be
  795.    compiled on FAT partitions with no modification. */
  796.  
  797.                 if ( (fp = fopen(tempstr, "r")) == NULL
  798.                      /* try original file name/not found: */
  799.                      && (!cp           /* if extension is ".tex[i[nfo]]... */
  800.                          || (!STREQ(cp,".tex") && !STREQ(cp,".texi")
  801.                              && !STREQ(cp,".texinfo"))
  802.                          || (!(strcpy(cp,".tex"),fp = fopen(tempstr,"r"))
  803.                              && !(fp = fopen(strcat(tempstr,"i"),"r"))
  804.                              && !(fp = fopen(strcat(tempstr,"nfo"),"r")) )) )
  805.                     /* ...try all three extensions */
  806.                     errormsg( "can't open included file ", tempstr);
  807.                 else {
  808.                     (void) process( fp, tempstr);
  809.                     (void) fclose( fp);
  810.                 }/* if */
  811.             }/* if */
  812.             break;
  813.  
  814.         case SETCLEAR:
  815.             PUSH( tptr);
  816.             if( !discarding ) {
  817.                 s = eatwhitespace( gettoken( eatwhitespace( s), macroarg[0]));
  818.                 /* get tag name */
  819.                 argmode = 1;          /* prepare for reading value */
  820.                 *macroarg[argmode]='\0';
  821.             }/* if */
  822.             break;
  823.  
  824.         case VALUE:
  825.             PUSH( tptr);
  826.             if( !discarding ) {
  827.                 s = gettoken( eatwhitespace( s), tempstr);
  828.                 cp = value( tempstr);
  829.                 if( cp!=NULL ) 
  830.                     OUTPUT( cp);
  831.                 /* endif */
  832.             }/* if */
  833.             break;
  834.  
  835.         case CONDITION:
  836.             PUSH( tptr);
  837.             if( !discarding ) 
  838.                 s = gettoken( eatwhitespace( s), tempstr);
  839.             /* endif */
  840.             tempstr[MAXTAGSIZE-1]='\0';
  841.             cp = value( tempstr);
  842.             if( (cp != NULL && *cp) ^ STREQ( token, "@ifclear") ) {
  843.                 OUTPUT( tptr->ipfstart);
  844.             } else {
  845.                 discarding = YES;
  846.                 discardlevel = stackptr;
  847.             }/* if */
  848.             break;
  849.  
  850.         case NODE:
  851.         case XREF:
  852.             PUSH( tptr);
  853.             if( !discarding ) {
  854.                 s = eatwhitespace( s);
  855.                 argmode=0;
  856.                 *macroarg[argmode]='\0';
  857.             }/* if */
  858.             break;
  859.  
  860.         case COMMENT:                /* avoid any mis-interpretation */
  861.             return "";               /* leave immediately */
  862.  
  863.         default:
  864.             /* can't happen */
  865.             errormsg( "ack ptui, what was that thing? ", token);
  866.         }/* switch */
  867.     }/* if */
  868.     return s;
  869. }/* interpret() */
  870.  
  871.  
  872. /*
  873.  * strpbrk_like - returns pointer to the leftmost occurrence in str of any
  874.  *     character in set, else pointer to terminating null.
  875. */
  876. char * strpbrk_like( char *str, char *set)
  877. {
  878.     static int inited_set = 0;
  879.     static char set_vec[256] = { 0 };
  880.  
  881.     if ( !inited_set ) { /* we *know* it'll be the same every time... */
  882.         while ( *set )
  883.             set_vec[(unsigned char)*set++] = 1;
  884.         set_vec[0] = 1;
  885.         inited_set = 1;
  886.     }/* if */
  887.     while ( set_vec[(int)*str] == 0 )
  888.         ++str;
  889.     /* endwhile */
  890.     return str;
  891. }/* strpbrk_like() */
  892.  
  893.  
  894. /*
  895.  * gettoken - fetch next token from input buffer. leave the input pointer
  896.  *     pointing to char after token.    may need to be modified when
  897.  *     new Texinfo commands are added which use different token boundaries.
  898.  *
  899.  *     will handle case where fgets() has split a long line in the middle
  900.  *     of a token, but the token will appear to have been divided by a blank
  901.  *
  902.  *     will expand tab characters to "stupid" 8-character-tabs, as they
  903.  *     would be created by an editor automatically "tabbing" lines.
  904.  */
  905.  
  906. char * gettoken( char *s, char *token)
  907. {
  908.     static char    endchars[] = " \n\t@{}:.*,";
  909.     char           *q, *t;
  910.     static int     xpos;
  911.  
  912.     if( s==NULL ) {                       /* Init new line */
  913.         xpos = 0;
  914.         return NULL;
  915.     }/* if */
  916.     q = s;
  917.     s = strpbrk_like( q, endchars);
  918.     if ( s != q ) {
  919.         switch ( *s ) {
  920.         case ' ':
  921.         case '\n':
  922.         case '\t':
  923.         case '@':
  924.         case '}':
  925.         case ':':
  926.         case '.':
  927.         case '*':
  928.         case '\0':
  929.         case ',':
  930.             --s;
  931.             break;
  932.         case '{':
  933.             break;
  934.         }/* switch */
  935.     } else {   /* *s == *q */
  936.         switch ( *s ) {
  937.         case ' ':
  938.         case '\n':
  939.         case '\t':
  940.         case '{':
  941.         case ':':
  942.         case '.':
  943.         case '*':
  944.         case '\0':
  945.             break;
  946.         case '}':
  947.             if ( *(s+1) == '{' ) /* footnotes with daggers and dbl daggers!! */
  948.                 ++s;
  949.             break;
  950.         case '@':
  951.             s = strpbrk_like( q + 1, endchars);
  952.             /* handles 2 char @ tokens: @{ @} @@ @: @. @* */
  953.             if ( strchr("{}@:.*", (int)*s) == NULL
  954.                  || (s > q+1 && (*s =='}' || *s == '@')) )
  955.                 --s;
  956.             break;
  957.         }/* switch */
  958.     }/* if */
  959.     for ( t = token; q <= s; ++q, ++t, ++xpos ) {
  960.         switch ( *q ) {
  961.         case ':':
  962.             strcpy( t, "&colon.");
  963.             t+=6;
  964.             break;
  965.         case '&':
  966.             strcpy( t, "&.");
  967.             t+=4;
  968.             break;
  969.         case '.':
  970.             strcpy( t, "&per.");
  971.             t+=4;
  972.             break;
  973.         case '\0':
  974.             *t = ' ';
  975.             break;
  976.         case '\t':
  977.             do {
  978.                 *(t++)=' ';           
  979.             } while ( (++xpos)%8 );  /* file with blanks up to next 8-tab */
  980.             t--; xpos--;             /* last inc will be done by loop */
  981.             break;
  982.         case '\'':
  983.             if( argmode >= 0 ) {
  984.                 *t = '_';             /* Change ' to _ in xref's */
  985.                 break;
  986.             }/* if */
  987.         default   :
  988.             *t = *q;
  989.             break;
  990.         }/* switch */
  991.     }/* for */
  992.     *t = 0;
  993.     return ++s;
  994. }/* gettoken() */
  995.  
  996.  
  997.