(root)/
texinfo-7.1/
info/
util.c
       1  /* util.c --  various utility functions
       2  
       3     Copyright 1993-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      17  
      18  #include "info.h"
      19  #include "session.h"
      20  #include "util.h"
      21  #include "tag.h"
      22  
      23  /* wrapper for asprintf */
      24  static int
      25  xvasprintf (char **ptr, const char *template, va_list ap)
      26  {
      27    int ret;
      28    ret = vasprintf (ptr, template, ap);
      29    if (ret < 0)
      30      abort (); /* out of memory */
      31    return ret;
      32  }
      33  
      34  int
      35  xasprintf (char **ptr, const char *template, ...)
      36  {
      37    va_list v;
      38    va_start (v, template);
      39    return xvasprintf (ptr, template, v);
      40  }
      41  
      42  /* Return the file buffer which belongs to WINDOW's node. */
      43  FILE_BUFFER *
      44  file_buffer_of_window (WINDOW *window)
      45  {
      46    /* If this window has no node, then it has no file buffer. */
      47    if (!window->node)
      48      return NULL;
      49  
      50    if (window->node->fullpath)
      51      return info_find_file (window->node->fullpath);
      52  
      53    return NULL;
      54  }
      55  
      56  /* Return "(FILENAME)NODENAME" for NODE, or just "NODENAME" if NODE's
      57     filename is not set.  Return value should not be freed. */
      58  char *
      59  node_printed_rep (NODE *node)
      60  {
      61    static char *rep;
      62  
      63    if (node->fullpath)
      64      {
      65        char *filename = filename_non_directory (node->fullpath);
      66        rep = xrealloc (rep, 1 + strlen (filename) + 1 + strlen (node->nodename) + 1);
      67        sprintf (rep, "(%s)%s", filename, node->nodename);
      68        return rep;
      69      }
      70    else
      71      return node->nodename;
      72  }
      73  
      74  
      75  /* Return a pointer to the part of PATHNAME that simply defines the file. */
      76  char *
      77  filename_non_directory (char *pathname)
      78  {
      79    register char *filename = pathname + strlen (pathname);
      80  
      81    if (HAVE_DRIVE (pathname))
      82      pathname += 2;
      83  
      84    while (filename > pathname && !IS_SLASH (filename[-1]))
      85      filename--;
      86  
      87    return filename;
      88  }
      89  
      90  /* Return non-zero if NODE is one especially created by Info. */
      91  int
      92  internal_info_node_p (NODE *node)
      93  {
      94    return (node != NULL) && (node->flags & N_IsInternal);
      95  }
      96  
      97  /* Make NODE appear to be one especially created by Info. */
      98  void
      99  name_internal_node (NODE *node, char *name)
     100  {
     101    if (!node)
     102      return;
     103  
     104    node->fullpath = "";
     105    node->subfile = 0;
     106    node->nodename = name;
     107    node->flags |= N_IsInternal;
     108  }
     109  
     110  /* Return the window displaying NAME, the name of an internally created
     111     Info window. */
     112  WINDOW *
     113  get_internal_info_window (char *name)
     114  {
     115    WINDOW *win;
     116  
     117    for (win = windows; win; win = win->next)
     118      if (internal_info_node_p (win->node) &&
     119          (strcmp (win->node->nodename, name) == 0))
     120        break;
     121  
     122    return win;
     123  }
     124  
     125  
     126  /* If ITER points to an ANSI escape sequence, process it, set PLEN to its
     127     length in bytes, and return 1.
     128     Otherwise, return 0.
     129   */
     130  int
     131  ansi_escape (mbi_iterator_t iter, size_t *plen)
     132  {
     133    if (raw_escapes_p && *mbi_cur_ptr (iter) == '\033' && mbi_avail (iter))
     134      {
     135        mbi_advance (iter);
     136        if (*mbi_cur_ptr (iter) == '[' &&  mbi_avail (iter))
     137          {
     138            ITER_SETBYTES (iter, 1);
     139            mbi_advance (iter);
     140            if (isdigit (*mbi_cur_ptr (iter)) && mbi_avail (iter))
     141              {	
     142                ITER_SETBYTES (iter, 1);
     143                mbi_advance (iter);
     144                if (*mbi_cur_ptr (iter) == 'm')
     145                  {
     146                    *plen = 4;
     147                    return 1;
     148                  }
     149                else if (isdigit (*mbi_cur_ptr (iter)) && mbi_avail (iter))
     150                  {
     151                    ITER_SETBYTES (iter, 1);
     152                    mbi_advance (iter);
     153                    if (*mbi_cur_ptr (iter) == 'm')
     154                      {
     155                        *plen = 5;
     156                        return 1;
     157                      }
     158                  }
     159              }
     160          }
     161      }
     162                  
     163    return 0;
     164  }
     165  
     166  static struct text_buffer printed_rep = { 0 };
     167  
     168  /* Return pointer to string that is the printed representation of character
     169     (or other logical unit) at ITER if it were printed at screen column
     170     PL_CHARS.  Use ITER_SETBYTES (util.h) on ITER if we need to advance 
     171     past a unit that the multibyte iteractor doesn't know about (like an ANSI 
     172     escape sequence).  If ITER points at an end-of-line character, set *DELIM to 
     173     this character.  *PCHARS gets the number of screen columns taken up by
     174     outputting the return value, and *PBYTES the number of bytes in returned
     175     string.  Return value is not null-terminated.  Return value must not be
     176     freed by caller. */
     177  char *
     178  printed_representation (mbi_iterator_t *iter, int *delim, size_t pl_chars,
     179                          size_t *pchars, size_t *pbytes) 
     180  {
     181    struct text_buffer *rep = &printed_rep;
     182  
     183    char *cur_ptr = (char *) mbi_cur_ptr (*iter);
     184    size_t cur_len = mb_len (mbi_cur (*iter));
     185  
     186    text_buffer_reset (&printed_rep);
     187  
     188    if (mb_isprint (mbi_cur (*iter)))
     189      {
     190        /* cur.wc gives a wchar_t object.  See mbiter.h in the
     191           gnulib/lib directory. */
     192        *pchars = wcwidth ((*iter).cur.wc);
     193        *pbytes = cur_len;
     194        return cur_ptr;
     195      }
     196    else if (cur_len == 1)
     197      {
     198        if (*cur_ptr == '\n' || *cur_ptr == '\r')
     199          {
     200            /* If this is a CRLF line ending, ignore this character. */
     201            if (*cur_ptr == '\r' && cur_ptr[1] == '\n')
     202              {
     203                *pchars = 0;
     204                *pbytes = 0;
     205                return cur_ptr;
     206              }
     207  
     208            *pchars = 1;
     209            *pbytes = cur_len;
     210            *delim = *cur_ptr;
     211            text_buffer_add_char (rep, ' ');
     212            return cur_ptr;
     213          }
     214        else if (ansi_escape (*iter, &cur_len))
     215          {
     216            *pchars = 0; 
     217            *pbytes = cur_len;
     218            ITER_SETBYTES (*iter, cur_len);
     219  
     220            return cur_ptr;
     221          }
     222        else if (*cur_ptr == '\t')
     223          {
     224            int i = 0;
     225  
     226            *pchars = ((pl_chars + 8) & 0xf8) - pl_chars;
     227            *pbytes = *pchars;
     228  
     229            /* We must output spaces instead of the tab because a tab may
     230               not clear characters already on the screen. */
     231            for (i = 0; i < *pbytes; i++)
     232              text_buffer_add_char (rep, ' ');
     233            return text_buffer_base (rep);
     234          }
     235      }
     236  
     237    /* Show CTRL-x as "^X".  */
     238    if (iscntrl (*cur_ptr) && *(unsigned char *)cur_ptr < 127)
     239      {
     240        *pchars = 2;
     241        *pbytes = 2;
     242        text_buffer_add_char (rep, '^');
     243        text_buffer_add_char (rep, *cur_ptr | 0x40);
     244        return text_buffer_base (rep);
     245      }
     246    else if (*cur_ptr == DEL)
     247      {
     248        *pchars = 0;
     249        *pbytes = 0;
     250        return text_buffer_base (rep);
     251      }
     252    else
     253      {
     254        /* Original byte was not recognized as anything.  Display its octal 
     255           value.  This could happen in the C locale for bytes above 128,
     256           or for bytes 128-159 in an ISO-8859-1 locale.  Don't output the bytes 
     257           as they are, because they could have special meaning to the 
     258           terminal. */
     259        *pchars = 4;
     260        *pbytes = 4;
     261        text_buffer_printf (rep, "\\%o", *(unsigned char *)cur_ptr);
     262        return text_buffer_base (rep);
     263      }
     264  }
     265  
     266  /* Flexible Text Buffer */
     267  
     268  void
     269  text_buffer_init (struct text_buffer *buf)
     270  {
     271    memset (buf, 0, sizeof *buf);
     272  }
     273  
     274  void
     275  text_buffer_free (struct text_buffer *buf)
     276  {
     277    free (buf->base);
     278  }
     279  
     280  size_t
     281  text_buffer_vprintf (struct text_buffer *buf, const char *format, va_list ap)
     282  {
     283    ssize_t n;
     284    va_list ap_copy;
     285  
     286    if (!buf->base)
     287      {
     288        if (buf->size == 0)
     289  	buf->size = MIN_TEXT_BUF_ALLOC; /* Initial allocation */
     290        
     291        buf->base = xmalloc (buf->size);
     292      }
     293    
     294    for (;;)
     295      {
     296        va_copy (ap_copy, ap);
     297        n = vsnprintf (buf->base + buf->off, buf->size - buf->off,
     298  		     format, ap_copy);
     299        va_end (ap_copy);
     300        if (n < 0 || buf->off + n >= buf->size ||
     301  	  !memchr (buf->base + buf->off, '\0', buf->size - buf->off + 1))
     302  	{
     303  	  size_t newlen = buf->size * 2;
     304  	  if (newlen < buf->size)
     305  	    xalloc_die ();
     306  	  buf->size = newlen;
     307  	  buf->base = xrealloc (buf->base, buf->size);
     308  	}
     309        else
     310  	{
     311  	  buf->off += n;
     312  	  break;
     313  	}
     314      }
     315    return n;
     316  }
     317  
     318  /* Make sure there are LEN free bytes at end of BUF. */
     319  void
     320  text_buffer_alloc (struct text_buffer *buf, size_t len)
     321  {
     322    if (buf->off + len > buf->size)
     323      {
     324        buf->size = buf->off + len;
     325        if (buf->size < MIN_TEXT_BUF_ALLOC)
     326  	buf->size = MIN_TEXT_BUF_ALLOC;
     327        buf->base = xrealloc (buf->base, buf->size);
     328      }
     329  }
     330  
     331  /* Return number of bytes that can be written to text buffer without
     332     reallocating the text buffer. */
     333  size_t
     334  text_buffer_space_left (struct text_buffer *buf)
     335  {
     336    /* buf->size is the offset of the first byte after the allocated space.
     337       buf->off is the offset of the first byte to be written to. */
     338    return buf->size - buf->off;
     339  }
     340  
     341  #if HAVE_ICONV
     342  
     343  /* Run iconv using text buffer as output buffer. */
     344  size_t
     345  text_buffer_iconv (struct text_buffer *buf, iconv_t iconv_state,
     346                     ICONV_CONST char **inbuf, size_t *inbytesleft)
     347  {
     348    size_t out_bytes_left;
     349    char *outptr;
     350    size_t iconv_ret;
     351    size_t extra_alloc = 1;
     352  
     353    while (1)
     354      {
     355        outptr = text_buffer_base (buf) + text_buffer_off (buf);
     356        out_bytes_left = text_buffer_space_left (buf);
     357  
     358        iconv_ret = iconv (iconv_state, inbuf, inbytesleft,
     359                           &outptr, &out_bytes_left);
     360        text_buffer_off (buf) = outptr - text_buffer_base (buf);
     361  
     362        if (iconv_ret != (size_t) -1)
     363          break; /* success */
     364  
     365        /* If we ran out of space, allocate more and try again. */
     366        if (errno == E2BIG)
     367          text_buffer_alloc (buf, (extra_alloc *= 4));
     368        else
     369          break; /* let calling code deal with it */
     370      }
     371    return iconv_ret;
     372  }
     373  
     374  #endif /* HAVE_ICONV */
     375  
     376  size_t
     377  text_buffer_add_string (struct text_buffer *buf, const char *str, size_t len)
     378  {
     379    text_buffer_alloc (buf, len);
     380    memcpy (buf->base + buf->off, str, len);
     381    buf->off += len;
     382    return len;
     383  }
     384  
     385  size_t
     386  text_buffer_fill (struct text_buffer *buf, int c, size_t len)
     387  {
     388    char *p;
     389    int i;
     390    
     391    text_buffer_alloc (buf, len);
     392    
     393    for (i = 0, p = buf->base + buf->off; i < len; i++)
     394      *p++ = c;
     395    buf->off += len;
     396    
     397    return len;
     398  }
     399  
     400  void
     401  text_buffer_add_char (struct text_buffer *buf, int c)
     402  {
     403    char ch = c;
     404    text_buffer_add_string (buf, &ch, 1);
     405  }
     406  
     407  size_t
     408  text_buffer_printf (struct text_buffer *buf, const char *format, ...)
     409  {
     410    va_list ap;
     411    size_t n;
     412    
     413    va_start (ap, format);
     414    n = text_buffer_vprintf (buf, format, ap);
     415    va_end (ap);
     416    return n;
     417  }
     418  
     419  #if defined(__MSDOS__) || defined(__MINGW32__)
     420  /* Cannot use FILENAME_CMP here, since that does not consider forward-
     421     and back-slash characters equal.  */
     422  int
     423  fncmp (const char *fn1, const char *fn2)
     424  {
     425    const char *s1 = fn1, *s2 = fn2;
     426  
     427    while (tolower (*s1) == tolower (*s2)
     428  	 || (IS_SLASH (*s1) && IS_SLASH (*s2)))
     429      {
     430        if (*s1 == 0)
     431  	return 0;
     432        s1++;
     433        s2++;
     434      }
     435  
     436    return tolower (*s1) - tolower (*s2);
     437  }
     438  
     439  #endif