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