[BACK]Return to eqn_html.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Diff for /mandoc/eqn_html.c between version 1.5 and 1.19

version 1.5, 2014/09/28 13:34:15 version 1.19, 2019/03/17 18:21:45
Line 1 
Line 1 
 /*      $Id$ */  /*      $Id$ */
 /*  /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
    * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 19 
Line 20 
 #include <sys/types.h>  #include <sys/types.h>
   
 #include <assert.h>  #include <assert.h>
   #include <ctype.h>
 #include <stdio.h>  #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   
 #include "mandoc.h"  #include "mandoc.h"
   #include "roff.h"
   #include "eqn.h"
 #include "out.h"  #include "out.h"
 #include "html.h"  #include "html.h"
   
 static  const enum htmltag fontmap[EQNFONT__MAX] = {  static void
         TAG_SPAN, /* EQNFONT_NONE */  eqn_box(struct html *p, const struct eqn_box *bp)
         TAG_SPAN, /* EQNFONT_ROMAN */  
         TAG_B, /* EQNFONT_BOLD */  
         TAG_B, /* EQNFONT_FAT */  
         TAG_I /* EQNFONT_ITALIC */  
 };  
   
 static const struct eqn_box *  
         eqn_box(struct html *, const struct eqn_box *, int);  
   
   
 void  
 print_eqn(struct html *p, const struct eqn *ep)  
 {  {
         struct htmlpair  tag;          struct tag      *post, *row, *cell, *t;
         struct tag      *t;          const struct eqn_box *child, *parent;
           const char      *cp;
           size_t           i, j, rows;
           enum htmltag     tag;
           enum eqn_fontt   font;
   
         PAIR_CLASS_INIT(&tag, "eqn");  
         t = print_otag(p, TAG_MATH, 1, &tag);  
   
         p->flags |= HTML_NONOSPACE;  
         eqn_box(p, ep->root, 1);  
         p->flags &= ~HTML_NONOSPACE;  
   
         print_tagq(p, t);  
 }  
   
 /*  
  * This function is fairly brittle.  
  * This is because the eqn syntax doesn't play so nicely with recusive  
  * formats, e.g.,  
  *     foo sub bar sub baz  
  * ...needs to resolve into  
  *     <msub> foo <msub> bar, baz </msub> </msub>  
  * In other words, we need to embed some recursive work.  
  * FIXME: this does NOT handle right-left associativity or precedence!  
  */  
 static const struct eqn_box *  
 eqn_box(struct html *p, const struct eqn_box *bp, int next)  
 {  
         struct tag      *post, *pilet, *tmp;  
         struct htmlpair  tag[2];  
         int              skiptwo;  
   
         if (NULL == bp)          if (NULL == bp)
                 return(NULL);                  return;
   
         post = pilet = NULL;          post = NULL;
         skiptwo = 0;  
   
         /*          /*
          * If we're a "row" under a pile, then open up the piling           * Special handling for a matrix, which is presented to us in
          * context here.           * column order, but must be printed in row-order.
          * We do this first because the pile surrounds the content of  
          * the contained expression.  
          */           */
         if (NULL != bp->parent && bp->parent->pile != EQNPILE_NONE) {          if (EQN_MATRIX == bp->type) {
                 pilet = print_otag(p, TAG_MTR, 0, NULL);                  if (NULL == bp->first)
                 print_otag(p, TAG_MTD, 0, NULL);                          goto out;
                   if (bp->first->type != EQN_LIST ||
                       bp->first->expectargs == 1) {
                           eqn_box(p, bp->first);
                           goto out;
                   }
                   if (NULL == (parent = bp->first->first))
                           goto out;
                   /* Estimate the number of rows, first. */
                   if (NULL == (child = parent->first))
                           goto out;
                   for (rows = 0; NULL != child; rows++)
                           child = child->next;
                   /* Print row-by-row. */
                   post = print_otag(p, TAG_MTABLE, "");
                   for (i = 0; i < rows; i++) {
                           parent = bp->first->first;
                           row = print_otag(p, TAG_MTR, "");
                           while (NULL != parent) {
                                   child = parent->first;
                                   for (j = 0; j < i; j++) {
                                           if (NULL == child)
                                                   break;
                                           child = child->next;
                                   }
                                   cell = print_otag(p, TAG_MTD, "");
                                   /*
                                    * If we have no data for this
                                    * particular cell, then print a
                                    * placeholder and continue--don't puke.
                                    */
                                   if (NULL != child)
                                           eqn_box(p, child->first);
                                   print_tagq(p, cell);
                                   parent = parent->next;
                           }
                           print_tagq(p, row);
                   }
                   goto out;
         }          }
   
         /*  
          * If we're establishing a pile, start the table mode now.  
          * If we've already in a pile row, then don't override "pilet",  
          * because we'll be closed out anyway.  
          */  
         if (bp->pile != EQNPILE_NONE) {  
                 tmp = print_otag(p, TAG_MTABLE, 0, NULL);  
                 pilet = (NULL == pilet) ? tmp : pilet;  
         }  
   
         /*  
          * Positioning.  
          * This is the most complicated part, and actually doesn't quite  
          * work (FIXME) because it doesn't account for associativity.  
          * Setting "post" will mean that we're only going to process a  
          * single or double following expression.  
          */  
         switch (bp->pos) {          switch (bp->pos) {
         case (EQNPOS_SUP):          case EQNPOS_TO:
                 post = print_otag(p, TAG_MSUP, 0, NULL);                  post = print_otag(p, TAG_MOVER, "");
                 break;                  break;
         case (EQNPOS_FROM):          case EQNPOS_SUP:
                 /* FALLTHROUGH */                  post = print_otag(p, TAG_MSUP, "");
         case (EQNPOS_SUB):  
                 post = print_otag(p, TAG_MSUB, 0, NULL);  
                 break;                  break;
         case (EQNPOS_OVER):          case EQNPOS_FROM:
                 post = print_otag(p, TAG_MFRAC, 0, NULL);                  post = print_otag(p, TAG_MUNDER, "");
                 break;                  break;
         case (EQNPOS_SUBSUP):          case EQNPOS_SUB:
                 /* This requires two elements. */                  post = print_otag(p, TAG_MSUB, "");
                 post = print_otag(p, TAG_MSUBSUP, 0, NULL);  
                 skiptwo = 1;  
                 break;                  break;
           case EQNPOS_OVER:
                   post = print_otag(p, TAG_MFRAC, "");
                   break;
           case EQNPOS_FROMTO:
                   post = print_otag(p, TAG_MUNDEROVER, "");
                   break;
           case EQNPOS_SUBSUP:
                   post = print_otag(p, TAG_MSUBSUP, "");
                   break;
           case EQNPOS_SQRT:
                   post = print_otag(p, TAG_MSQRT, "");
                   break;
         default:          default:
                 break;                  break;
         }          }
   
         /*t = EQNFONT_NONE == bp->font ? NULL :          if (bp->top || bp->bottom) {
             print_otag(p, fontmap[(int)bp->font], 0, NULL);*/                  assert(NULL == post);
                   if (bp->top && NULL == bp->bottom)
                           post = print_otag(p, TAG_MOVER, "");
                   else if (bp->top && bp->bottom)
                           post = print_otag(p, TAG_MUNDEROVER, "");
                   else if (bp->bottom)
                           post = print_otag(p, TAG_MUNDER, "");
           }
   
         if (NULL != bp->text) {          if (EQN_PILE == bp->type) {
                 assert(NULL == bp->first);                  assert(NULL == post);
                 /*                  if (bp->first != NULL &&
                  * We have text.                      bp->first->type == EQN_LIST &&
                  * This can be a number, a function, a variable, or                      bp->first->expectargs > 1)
                  * pretty much anything else.                          post = print_otag(p, TAG_MTABLE, "");
                  * First, check for some known functions.          } else if (bp->type == EQN_LIST && bp->expectargs > 1 &&
                  * If we're going to create a structural node (e.g.,              bp->parent && bp->parent->type == EQN_PILE) {
                  * sqrt), then set the "post" variable only if it's not                  assert(NULL == post);
                  * already set.                  post = print_otag(p, TAG_MTR, "");
                  */                  print_otag(p, TAG_MTD, "");
                 if (0 == strcmp(bp->text, "sqrt")) {          }
                         tmp = print_otag(p, TAG_MSQRT, 0, NULL);  
                         post = (NULL == post) ? tmp : post;          if (bp->text != NULL) {
                 } else if (0 == strcmp(bp->text, "+") ||                  assert(post == NULL);
                            0 == strcmp(bp->text, "-") ||                  tag = TAG_MI;
                            0 == strcmp(bp->text, "=") ||                  cp = bp->text;
                            0 == strcmp(bp->text, "(") ||                  if (isdigit((unsigned char)cp[0]) ||
                            0 == strcmp(bp->text, ")") ||                      (cp[0] == '.' && isdigit((unsigned char)cp[1]))) {
                            0 == strcmp(bp->text, "/")) {                          tag = TAG_MN;
                         tmp = print_otag(p, TAG_MO, 0, NULL);                          while (*++cp != '\0') {
                         print_text(p, bp->text);                                  if (*cp != '.' &&
                         print_tagq(p, tmp);                                      isdigit((unsigned char)*cp) == 0) {
                 } else {                                          tag = TAG_MI;
                         tmp = print_otag(p, TAG_MI, 0, NULL);                                          break;
                         print_text(p, bp->text);                                  }
                         print_tagq(p, tmp);                          }
                   } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) {
                           tag = TAG_MO;
                           while (*cp != '\0') {
                                   if (cp[0] == '\\' && cp[1] != '\0') {
                                           cp++;
                                           mandoc_escape(&cp, NULL, NULL);
                                   } else if (isalnum((unsigned char)*cp)) {
                                           tag = TAG_MI;
                                           break;
                                   } else
                                           cp++;
                           }
                 }                  }
         } else if (NULL != bp->first) {                  font = bp->font;
                 assert(NULL == bp->text);                  if (bp->text[0] != '\0' &&
                 /*                      (((tag == TAG_MN || tag == TAG_MO) &&
                  * If we're a "fenced" component (i.e., having                        font == EQNFONT_ROMAN) ||
                  * brackets), then process those brackets now.                       (tag == TAG_MI && font == (bp->text[1] == '\0' ?
                  * Otherwise, introduce a dummy row (if we're not                        EQNFONT_ITALIC : EQNFONT_ROMAN))))
                  * already in a table context).                          font = EQNFONT_NONE;
                  */                  switch (font) {
                 tmp = NULL;                  case EQNFONT_NONE:
                 if (NULL != bp->left || NULL != bp->right) {                          post = print_otag(p, tag, "");
                         PAIR_INIT(&tag[0], ATTR_OPEN,                          break;
                                 NULL != bp->left ? bp->left : "");                  case EQNFONT_ROMAN:
                         PAIR_INIT(&tag[1], ATTR_CLOSE,                          post = print_otag(p, tag, "?", "fontstyle", "normal");
                                 NULL != bp->right ? bp->right : "");                          break;
                         tmp = print_otag(p, TAG_MFENCED, 2, tag);                  case EQNFONT_BOLD:
                         print_otag(p, TAG_MROW, 0, NULL);                  case EQNFONT_FAT:
                 } else if (NULL == pilet)                          post = print_otag(p, tag, "?", "fontweight", "bold");
                         tmp = print_otag(p, TAG_MROW, 0, NULL);                          break;
                 eqn_box(p, bp->first, 1);                  case EQNFONT_ITALIC:
                 if (NULL != tmp)                          post = print_otag(p, tag, "?", "fontstyle", "italic");
                         print_tagq(p, tmp);                          break;
                   default:
                           abort();
                   }
                   print_text(p, bp->text);
           } else if (NULL == post) {
                   if (NULL != bp->left || NULL != bp->right)
                           post = print_otag(p, TAG_MFENCED, "??",
                               "open", bp->left == NULL ? "" : bp->left,
                               "close", bp->right == NULL ? "" : bp->right);
                   if (NULL == post)
                           post = print_otag(p, TAG_MROW, "");
                   else
                           print_otag(p, TAG_MROW, "");
         }          }
   
         /*          eqn_box(p, bp->first);
          * If a positional context, invoke the "next" context.  
          * This is recursive and will return the end of the recursive  out:
          * chain of "next" contexts.          if (NULL != bp->bottom) {
          */                  t = print_otag(p, TAG_MO, "");
         if (NULL != post) {                  print_text(p, bp->bottom);
                 bp = eqn_box(p, bp->next, 0);                  print_tagq(p, t);
                 if (skiptwo)  
                         bp = eqn_box(p, bp->next, 0);  
                 print_tagq(p, post);  
         }          }
           if (NULL != bp->top) {
                   t = print_otag(p, TAG_MO, "");
                   print_text(p, bp->top);
                   print_tagq(p, t);
           }
   
         /*          if (NULL != post)
          * If we're being piled (either directly, in the table, or                  print_tagq(p, post);
          * indirectly in a table row), then close that out.  
          */  
         if (NULL != pilet)  
                 print_tagq(p, pilet);  
   
         /*          eqn_box(p, bp->next);
          * If we're normally processing, then grab the next node.  }
          * If we're in a recursive context, then don't seek to the next  
          * node; further recursion has already been handled.  void
          */  print_eqn(struct html *p, const struct eqn_box *bp)
         return(next ? eqn_box(p, bp->next, 1) : bp);  {
           struct tag      *t;
   
           if (bp->first == NULL)
                   return;
   
           t = print_otag(p, TAG_MATH, "c", "eqn");
   
           p->flags |= HTML_NONOSPACE;
           eqn_box(p, bp);
           p->flags &= ~HTML_NONOSPACE;
   
           print_tagq(p, t);
 }  }

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.19

CVSweb