1  /* Copyright (C) 1996-2023 Free Software Foundation, Inc.
       2     Copyright The GNU Toolchain Authors.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library 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 GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <assert.h>
      20  #include <dlfcn.h>
      21  #include <errno.h>
      22  #include <gconv.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <wchar.h>
      26  #include <wcsmbsload.h>
      27  
      28  #include <pointer_guard.h>
      29  
      30  #ifndef EILSEQ
      31  # define EILSEQ EINVAL
      32  #endif
      33  
      34  
      35  /* This is the private state used if PS is NULL.  */
      36  static mbstate_t state;
      37  
      38  size_t
      39  __wcrtomb_internal (char *s, wchar_t wc, mbstate_t *ps, size_t s_size)
      40  {
      41    char buf[MB_LEN_MAX];
      42    struct __gconv_step_data data;
      43    int status;
      44    size_t result;
      45    size_t dummy;
      46    const struct gconv_fcts *fcts;
      47  
      48    /* Set information for this step.  */
      49    data.__invocation_counter = 0;
      50    data.__internal_use = 1;
      51    data.__flags = __GCONV_IS_LAST;
      52    data.__statep = ps ?: &state;
      53  
      54    /* A first special case is if S is NULL.  This means put PS in the
      55       initial state.  */
      56    if (s == NULL)
      57      wc = L'\0';
      58  
      59    /* Tell where we want to have the result.  */
      60    data.__outbuf = (unsigned char *) buf;
      61    data.__outbufend = (unsigned char *) buf + sizeof buf;
      62  
      63    /* Get the conversion functions.  */
      64    fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
      65    __gconv_fct fct = fcts->tomb->__fct;
      66    if (fcts->tomb->__shlib_handle != NULL)
      67      PTR_DEMANGLE (fct);
      68  
      69    /* If WC is the NUL character we write into the output buffer the byte
      70       sequence necessary for PS to get into the initial state, followed
      71       by a NUL byte.  */
      72    if (wc == L'\0')
      73      {
      74        status = DL_CALL_FCT (fct, (fcts->tomb, &data, NULL, NULL,
      75  				  NULL, &dummy, 1, 1));
      76  
      77        if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT)
      78  	*data.__outbuf++ = '\0';
      79      }
      80    else
      81      {
      82        /* Do a normal conversion.  */
      83        const unsigned char *inbuf = (const unsigned char *) &wc;
      84  
      85        status = DL_CALL_FCT (fct,
      86  			    (fcts->tomb, &data, &inbuf,
      87  			     inbuf + sizeof (wchar_t), NULL, &dummy, 0, 1));
      88      }
      89  
      90    /* There must not be any problems with the conversion but illegal input
      91       characters.  The output buffer must be large enough, otherwise the
      92       definition of MB_CUR_MAX is not correct.  All the other possible
      93       errors also must not happen.  */
      94    assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
      95  	  || status == __GCONV_ILLEGAL_INPUT
      96  	  || status == __GCONV_INCOMPLETE_INPUT
      97  	  || status == __GCONV_FULL_OUTPUT);
      98  
      99    if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
     100        || status == __GCONV_FULL_OUTPUT)
     101      {
     102        result = data.__outbuf - (unsigned char *) buf;
     103  
     104        if (s != NULL)
     105  	{
     106  	  if (result > s_size)
     107  	    __chk_fail ();
     108  
     109  	  memcpy (s, buf, result);
     110  	}
     111      }
     112    else
     113      {
     114        result = (size_t) -1;
     115        __set_errno (EILSEQ);
     116      }
     117  
     118    return result;
     119  }
     120  
     121  size_t
     122  __wcrtomb (char *s, wchar_t wc, mbstate_t *ps)
     123  {
     124    return __wcrtomb_internal (s, wc, ps, (size_t) -1);
     125  }
     126  weak_alias (__wcrtomb, wcrtomb)
     127  libc_hidden_weak (wcrtomb)