(root)/
gcc-13.2.0/
gcc/
testsuite/
gcc.dg/
analyzer/
pr93355-localealias.c
       1  /* Integration test to ensure we issue a FILE * leak diagnostic for
       2     this particular non-trivial case.
       3     Adapted from intl/localealias.c, with all #includes removed.  */
       4  
       5  /* { dg-do "compile" } */
       6  /* { dg-additional-options "-Wno-analyzer-too-complex" } */
       7  /* TODO: remove the need for this option.  */
       8  /* { dg-require-effective-target alloca } */
       9  
      10  /* Handle aliases for locale names.
      11     Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
      12  
      13     This program is free software; you can redistribute it and/or modify it
      14     under the terms of the GNU Library General Public License as published
      15     by the Free Software Foundation; either version 2, or (at your option)
      16     any later version.
      17  
      18     This program is distributed in the hope that it will be useful,
      19     but WITHOUT ANY WARRANTY; without even the implied warranty of
      20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      21     Library General Public License for more details.
      22  
      23     You should have received a copy of the GNU Library General Public
      24     License along with this program; if not, write to the Free Software
      25     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
      26     USA.  */
      27  
      28  /* Minimal version of system headers.  */
      29  
      30  typedef __SIZE_TYPE__ size_t;
      31  #define NULL ((void *) 0)
      32  
      33  #define PATH_SEPARATOR ':'
      34  typedef struct _IO_FILE FILE;
      35  extern FILE *fopen(const char *__restrict __filename,
      36  		   const char *__restrict __modes);
      37  extern int feof_unlocked(FILE *__stream) __attribute__((__nothrow__, __leaf__));
      38  extern char *fgets_unlocked(char *__restrict __s, int __n,
      39  			    FILE *__restrict __stream);
      40  extern int fclose(FILE *__stream);
      41  
      42  #define alloca __builtin_alloca
      43  
      44  extern char *strchr(const char *__s, int __c)
      45      __attribute__((__nothrow__, __leaf__)) __attribute__((__pure__))
      46      __attribute__((__nonnull__(1)));
      47  extern void *memcpy(void *__restrict __dest, const void *__restrict __src,
      48  		    size_t __n) __attribute__((__nothrow__, __leaf__))
      49    __attribute__((__nonnull__(1, 2)));
      50  extern void *mempcpy(void *__restrict __dest, const void *__restrict __src,
      51  		     size_t __n) __attribute__((__nothrow__, __leaf__))
      52    __attribute__((__nonnull__(1, 2)));
      53  #define HAVE_MEMPCPY 1
      54  extern size_t strlen(const char *__s) __attribute__((__nothrow__, __leaf__))
      55    __attribute__((__pure__)) __attribute__((__nonnull__(1)));
      56  
      57  extern int strcasecmp(const char *__s1, const char *__s2)
      58      __attribute__((__nothrow__, __leaf__)) __attribute__((__pure__))
      59      __attribute__((__nonnull__(1, 2)));
      60  
      61  extern int isspace(int) __attribute__((__nothrow__, __leaf__));
      62  
      63  extern void *realloc(void *__ptr, size_t __size)
      64      __attribute__((__nothrow__, __leaf__))
      65      __attribute__((__warn_unused_result__));
      66  
      67  typedef int (*__compar_fn_t)(const void *, const void *);
      68  extern void *bsearch(const void *__key, const void *__base, size_t __nmemb,
      69  		     size_t __size, __compar_fn_t __compar)
      70      __attribute__((__nonnull__(1, 2, 5)));
      71  
      72  extern __inline __attribute__((__gnu_inline__)) void *
      73  bsearch(const void *__key, const void *__base, size_t __nmemb, size_t __size,
      74  	__compar_fn_t __compar) {
      75    size_t __l, __u, __idx;
      76    const void *__p;
      77    int __comparison;
      78  
      79    __l = 0;
      80    __u = __nmemb;
      81    while (__l < __u) {
      82      __idx = (__l + __u) / 2;
      83      __p = (void *)(((const char *)__base) + (__idx * __size));
      84      __comparison = (*__compar)(__key, __p);
      85      if (__comparison < 0)
      86        __u = __idx;
      87      else if (__comparison > 0)
      88        __l = __idx + 1;
      89      else
      90        return (void *)__p;
      91    }
      92  
      93    return ((void *)0);
      94  }
      95  
      96  extern void qsort(void *__base, size_t __nmemb, size_t __size,
      97  		  __compar_fn_t __compar) __attribute__((__nonnull__(1, 4)));
      98  
      99  /* Minimal version of intl headers.  */
     100  
     101  #define PARAMS(args) args
     102  
     103  #define relocate libintl_relocate
     104  extern const char *libintl_relocate(const char *pathname);
     105  
     106  #define LOCALE_ALIAS_PATH "value for LOCALE_ALIAS_PATH"
     107  
     108  /* Cleaned-up body of localealias.c follows.  */
     109  
     110  #ifndef internal_function
     111  # define internal_function
     112  #endif
     113  
     114  /* Some optimizations for glibc.  */
     115  # define FEOF(fp)		feof_unlocked (fp)
     116  # define FGETS(buf, n, fp)	fgets_unlocked (buf, n, fp)
     117  
     118  /* For those losing systems which don't have `alloca' we have to add
     119     some additional code emulating it.  */
     120  # define freea(p) /* nothing */
     121  
     122  struct alias_map
     123  {
     124    const char *alias;
     125    const char *value;
     126  };
     127  
     128  # define libc_freeres_ptr(decl) decl
     129  
     130  libc_freeres_ptr (static char *string_space);
     131  static size_t string_space_act;
     132  static size_t string_space_max;
     133  libc_freeres_ptr (static struct alias_map *map);
     134  static size_t nmap;
     135  static size_t maxmap;
     136  
     137  
     138  /* Prototypes for local functions.  */
     139  static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
     140       internal_function;
     141  static int extend_alias_table PARAMS ((void));
     142  static int alias_compare PARAMS ((const struct alias_map *map1,
     143  				  const struct alias_map *map2));
     144  
     145  
     146  const char *
     147  _nl_expand_alias (name)
     148      const char *name;
     149  {
     150    static const char *locale_alias_path;
     151    struct alias_map *retval;
     152    const char *result = NULL;
     153    size_t added;
     154  
     155  #ifdef _LIBC
     156    __libc_lock_lock (lock);
     157  #endif
     158  
     159    if (locale_alias_path == NULL)
     160      locale_alias_path = LOCALE_ALIAS_PATH;
     161  
     162    do
     163      {
     164        struct alias_map item;
     165  
     166        item.alias = name;
     167  
     168        if (nmap > 0)
     169  	retval = (struct alias_map *) bsearch (&item, map, nmap,
     170  					       sizeof (struct alias_map),
     171  					       (int (*) PARAMS ((const void *,
     172  								 const void *))
     173  						) alias_compare);
     174        else
     175  	retval = NULL;
     176  
     177        /* We really found an alias.  Return the value.  */
     178        if (retval != NULL)
     179  	{
     180  	  result = retval->value;
     181  	  break;
     182  	}
     183  
     184        /* Perhaps we can find another alias file.  */
     185        added = 0;
     186        while (added == 0 && locale_alias_path[0] != '\0')
     187  	{
     188  	  const char *start;
     189  
     190  	  while (locale_alias_path[0] == PATH_SEPARATOR)
     191  	    ++locale_alias_path;
     192  	  start = locale_alias_path;
     193  
     194  	  while (locale_alias_path[0] != '\0'
     195  		 && locale_alias_path[0] != PATH_SEPARATOR)
     196  	    ++locale_alias_path;
     197  
     198  	  if (start < locale_alias_path)
     199  	    added = read_alias_file (start, locale_alias_path - start);
     200  	}
     201      }
     202    while (added != 0);
     203  
     204  #ifdef _LIBC
     205    __libc_lock_unlock (lock);
     206  #endif
     207  
     208    return result;
     209  }
     210  
     211  
     212  static size_t
     213  internal_function
     214  read_alias_file (fname, fname_len)
     215       const char *fname;
     216       int fname_len;
     217  {
     218    FILE *fp;
     219    char *full_fname;
     220    size_t added;
     221    static const char aliasfile[] = "/locale.alias";
     222  
     223    full_fname = (char *) alloca (fname_len + sizeof aliasfile);
     224  #ifdef HAVE_MEMPCPY
     225    mempcpy (mempcpy (full_fname, fname, fname_len),
     226  	   aliasfile, sizeof aliasfile);
     227  #else
     228    memcpy (full_fname, fname, fname_len);
     229    memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
     230  #endif
     231  
     232    fp = fopen (relocate (full_fname), "r"); /* { dg-message "opened here" } */
     233    freea (full_fname);
     234    if (fp == NULL)
     235      return 0;
     236  
     237  #ifdef HAVE___FSETLOCKING
     238    /* No threads present.  */
     239    __fsetlocking (fp, FSETLOCKING_BYCALLER);
     240  #endif
     241  
     242    added = 0;
     243    while (!FEOF (fp))
     244      {
     245        /* It is a reasonable approach to use a fix buffer here because
     246  	 a) we are only interested in the first two fields
     247  	 b) these fields must be usable as file names and so must not
     248  	    be that long
     249  	 We avoid a multi-kilobyte buffer here since this would use up
     250  	 stack space which we might not have if the program ran out of
     251  	 memory.  */
     252        char buf[400];
     253        char *alias;
     254        char *value;
     255        char *cp;
     256  
     257        if (FGETS (buf, sizeof buf, fp) == NULL)
     258  	/* EOF reached.  */
     259  	break;
     260  
     261        cp = buf;
     262        /* Ignore leading white space.  */
     263        while (isspace ((unsigned char) cp[0]))
     264  	++cp;
     265  
     266        /* A leading '#' signals a comment line.  */
     267        if (cp[0] != '\0' && cp[0] != '#')
     268  	{
     269  	  alias = cp++;
     270  	  while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
     271  	    ++cp;
     272  	  /* Terminate alias name.  */
     273  	  if (cp[0] != '\0')
     274  	    *cp++ = '\0';
     275  
     276  	  /* Now look for the beginning of the value.  */
     277  	  while (isspace ((unsigned char) cp[0]))
     278  	    ++cp;
     279  
     280  	  if (cp[0] != '\0')
     281  	    {
     282  	      size_t alias_len;
     283  	      size_t value_len;
     284  
     285  	      value = cp++;
     286  	      while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
     287  		++cp;
     288  	      /* Terminate value.  */
     289  	      if (cp[0] == '\n')
     290  		{
     291  		  /* This has to be done to make the following test
     292  		     for the end of line possible.  We are looking for
     293  		     the terminating '\n' which do not overwrite here.  */
     294  		  *cp++ = '\0';
     295  		  *cp = '\n';
     296  		}
     297  	      else if (cp[0] != '\0')
     298  		*cp++ = '\0';
     299  
     300  	      if (nmap >= maxmap)
     301  		if (__builtin_expect (extend_alias_table (), 0))
     302  		  return added; /* { dg-warning "leak of FILE 'fp'" } */
     303  
     304  	      alias_len = strlen (alias) + 1;
     305  	      value_len = strlen (value) + 1;
     306  
     307  	      if (string_space_act + alias_len + value_len > string_space_max)
     308  		{
     309  		  /* Increase size of memory pool.  */
     310  		  size_t new_size = (string_space_max
     311  				     + (alias_len + value_len > 1024
     312  					? alias_len + value_len : 1024));
     313  		  char *new_pool = (char *) realloc (string_space, new_size);
     314  		  if (new_pool == NULL)
     315  		    return added;
     316  
     317  		  if (__builtin_expect (string_space != new_pool, 0))
     318  		    {
     319  		      size_t i;
     320  
     321  		      for (i = 0; i < nmap; i++)
     322  			{
     323  			  map[i].alias += new_pool - string_space;
     324  			  map[i].value += new_pool - string_space;
     325  			}
     326  		    }
     327  
     328  		  string_space = new_pool;
     329  		  string_space_max = new_size;
     330  		}
     331  
     332  	      map[nmap].alias = memcpy (&string_space[string_space_act],
     333  					alias, alias_len);
     334  	      string_space_act += alias_len;
     335  
     336  	      map[nmap].value = memcpy (&string_space[string_space_act],
     337  					value, value_len);
     338  	      string_space_act += value_len;
     339  
     340  	      ++nmap;
     341  	      ++added;
     342  	    }
     343  	}
     344  
     345        /* Possibly not the whole line fits into the buffer.  Ignore
     346  	 the rest of the line.  */
     347        while (strchr (buf, '\n') == NULL)
     348  	if (FGETS (buf, sizeof buf, fp) == NULL)
     349  	  /* Make sure the inner loop will be left.  The outer loop
     350  	     will exit at the `feof' test.  */
     351  	  break;
     352      }
     353  
     354    /* Should we test for ferror()?  I think we have to silently ignore
     355       errors.  --drepper  */
     356    fclose (fp);
     357  
     358    if (added > 0)
     359      qsort (map, nmap, sizeof (struct alias_map),
     360  	   (int (*) PARAMS ((const void *, const void *))) alias_compare);
     361  
     362    return added;
     363  }
     364  
     365  
     366  static int
     367  extend_alias_table ()
     368  {
     369    size_t new_size;
     370    struct alias_map *new_map;
     371  
     372    new_size = maxmap == 0 ? 100 : 2 * maxmap;
     373    new_map = (struct alias_map *) realloc (map, (new_size
     374  						* sizeof (struct alias_map)));
     375    if (new_map == NULL)
     376      /* Simply don't extend: we don't have any more core.  */
     377      return -1;
     378  
     379    map = new_map;
     380    maxmap = new_size;
     381    return 0;
     382  }
     383  
     384  
     385  static int
     386  alias_compare (map1, map2)
     387       const struct alias_map *map1;
     388       const struct alias_map *map2;
     389  {
     390    return strcasecmp (map1->alias, map2->alias);
     391  }