(root)/
texinfo-7.1/
info/
tag.c
       1  /* tag.c -- Functions to handle Info tags (that is, the special
       2     construct for images, not the "tag table" of starting position.)
       3  
       4     Copyright 2012-2023 Free Software Foundation, Inc.
       5  
       6     This program is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation, either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18  
      19  #include "info.h"
      20  #include "tag.h" 
      21  #include "scan.h"
      22  #include "util.h"
      23  
      24  struct tag_handler
      25  {
      26    const char *name;
      27    size_t len;
      28    int (*handler) (char *, struct text_buffer *);
      29  };
      30  
      31  struct info_tag
      32  {
      33    struct info_tag *next;
      34    char *kw;
      35    char *val;
      36  };
      37  
      38  static void
      39  info_tag_free (struct info_tag *tag)
      40  {
      41    while (tag)
      42      {
      43        struct info_tag *next = tag->next;
      44        free (tag->kw);
      45        free (tag->val);
      46        free (tag);
      47        tag = next;
      48      }
      49  }
      50  
      51  
      52  /* See if KW is one of the tags in the list starting at TAG.  */
      53  
      54  static struct info_tag *
      55  info_tag_find (struct info_tag *tag, const char *kw)
      56  {
      57    for (; tag; tag = tag->next)
      58      if (strcmp (tag->kw, kw) == 0)
      59        return tag;
      60    return NULL;
      61  }
      62  
      63  
      64  /* Found a keyword when parsing the full tag string: alt, text, etc.
      65     Return the new tag, update *TMPBUF_PTR and set *KW.  */
      66  
      67  static struct info_tag *
      68  tag_found_keyword (struct text_buffer *tmpbuf_ptr, char **kw)
      69  {
      70    struct info_tag *tag = xmalloc (sizeof (*tag));
      71    tag->next = NULL;  /* have to update in caller */
      72  
      73    text_buffer_add_char (tmpbuf_ptr, 0);
      74    if (*kw != tmpbuf_ptr->base) { /* in case tmpbuf got realloc-ed */
      75      *kw = tmpbuf_ptr->base;      /* ick */
      76    }
      77    tag->kw = xstrdup (*kw);
      78    tag->val = xstrdup (*kw + strlen(*kw) + 1);
      79    text_buffer_reset (tmpbuf_ptr);
      80  
      81    return tag;
      82  }
      83  
      84  /* Handle the image tag.  */
      85  
      86  static int
      87  tag_image (char *text, struct text_buffer *outbuf)
      88  {
      89    mbi_iterator_t iter;
      90    enum { state_kw, state_val, state_qstr, state_delim } state = state_kw;
      91    struct text_buffer tmpbuf;
      92    char *kw;
      93    struct info_tag *tag_head = NULL, *tag;
      94    int escaped = 0;
      95    
      96    text_buffer_init (&tmpbuf);
      97    for (mbi_init (iter, text, strlen (text)); mbi_avail (iter);
      98         mbi_advance (iter))
      99      {
     100        const char *cur_ptr;
     101        size_t cur_len;
     102        
     103        if (mb_isspace (mbi_cur (iter)))
     104  	{
     105  	  if (state == state_val)
     106  	    {
     107                struct info_tag *new_kw = tag_found_keyword (&tmpbuf, &kw);
     108                new_kw->next = tag_head;
     109                tag_head = new_kw;
     110                state = state_delim;
     111                continue;
     112  	    }
     113  	  if (state == state_delim)
     114  	    continue;
     115  	}
     116        else if (state == state_delim)
     117  	state = state_kw;
     118        cur_len = mb_len (mbi_cur (iter));
     119        cur_ptr = mbi_cur_ptr (iter);
     120        
     121        if (state == state_qstr && escaped)
     122  	{
     123  	  escaped = 0;
     124  	}
     125        else if (cur_len == 1)
     126  	{
     127  	  switch (*cur_ptr)
     128  	    {
     129  	    case '=':
     130  	      if (state != state_kw)
     131  		break;
     132  	      text_buffer_add_char (&tmpbuf, 0);
     133  	      kw = tmpbuf.base;
     134  	      if (!mbi_avail (iter))
     135  		break;
     136  	      mbi_advance (iter);
     137  	      state = state_val;
     138  	      cur_len = mb_len (mbi_cur (iter));
     139  	      cur_ptr = mbi_cur_ptr (iter);
     140  	      if (!(cur_len == 1 && *cur_ptr == '"'))
     141  		break;
     142  	      /* fall through */
     143  
     144  	    case '"':
     145  	      if (state == state_val)
     146  		{
     147  		  state = state_qstr;
     148  		  continue;
     149  		}
     150  	      if (state == state_qstr)
     151  		{
     152  		  struct info_tag *new_kw = tag_found_keyword (&tmpbuf, &kw);
     153  		  new_kw->next = tag_head;
     154  		  tag_head = new_kw;
     155  		  state = state_delim;
     156  		  continue;
     157  		}
     158  	      break;
     159  
     160  	    case '\\':
     161  	      if (state == state_qstr)
     162  		{
     163  		  escaped = 1;
     164  		  continue;
     165  		}
     166  	    }
     167  	}
     168        text_buffer_add_string (&tmpbuf, cur_ptr, cur_len);
     169      }
     170  
     171    tag = info_tag_find (tag_head, "text");
     172    if (!tag)
     173      tag = info_tag_find (tag_head, "alt");
     174  
     175    if (tag)
     176      {
     177        text_buffer_add_string (outbuf, tag->val, strlen (tag->val));
     178      }
     179    
     180    text_buffer_free (&tmpbuf);
     181    info_tag_free (tag_head);
     182    return 0;
     183  }
     184  
     185  
     186  /* We don't do anything with the index tag; it'll just be ignored.  */
     187  
     188  static struct tag_handler tagtab[] = {
     189    { "image", 5, tag_image },
     190    { "index", 5, NULL },
     191    { NULL }
     192  };
     193  
     194  static struct tag_handler *
     195  find_tag_handler (char *tag, size_t taglen)
     196  {
     197    struct tag_handler *tp;
     198  
     199    for (tp = tagtab; tp->name; tp++)
     200      if (taglen >= tp->len && strncmp (tp->name, tag, tp->len) == 0)
     201        return tp;
     202    return NULL;
     203  }
     204  
     205  /* Expand \b[...\b] construct at *INPUT.  If encountered, append the
     206     expanded text to OUTBUF, advance *INPUT past the tag, and return 1.
     207     Otherwise, return 0.  If it is an index tag, set IS_INDEX to 1.
     208     *INPUT points into a null-terminated area which may however contain other 
     209     null characters.  INPUT_END points to the end of this area. */
     210  int
     211  tag_expand (char **input, char *input_end,
     212              struct text_buffer *outbuf, int *is_index)
     213  {
     214    char *p = *input;
     215    char *q;
     216    size_t len;
     217    struct tag_handler *tp;
     218  
     219    if (p >= input_end - 3
     220      || memcmp(p, "\0\b[", 3) != 0)       /* opening magic? */
     221      return 0;
     222  
     223    p += 3;
     224    q = p + strlen (p);
     225    if (q >= input_end - 3
     226        || memcmp (q + 1, "\b]", 2)) /* closing magic? */
     227      return 0; /* Not a proper tag. */
     228  
     229    /* Output is different for index nodes */
     230    if (!strncmp ("index", p, strlen ("index")))
     231      *is_index = 1;
     232  
     233    len = strcspn (p, " \t");       /* tag name */
     234    tp = find_tag_handler (p, len);
     235    if (tp && tp->handler)
     236      {
     237        while (p[len] == ' ' || p[len] == '\t')
     238          ++len;                      /* move past whitespace */
     239    
     240        tp->handler (p + len, outbuf);
     241      }
     242    *input = q + 3;
     243    return 1;
     244  }