LCOV - code coverage report
Current view: top level - glib - gprint.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 22.6 % 31 7
Test Date: 2026-01-20 05:15:58 Functions: 66.7 % 3 2
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright © 2025 Luca Bacci
       3                 :             :  *
       4                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       5                 :             :  *
       6                 :             :  * This library is free software; you can redistribute it and/or
       7                 :             :  * modify it under the terms of the GNU Lesser General Public
       8                 :             :  * License as published by the Free Software Foundation; either
       9                 :             :  * version 2.1 of the License, or (at your option) any later version.
      10                 :             :  *
      11                 :             :  * This library 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 GNU
      14                 :             :  * Lesser General Public License for more details.
      15                 :             :  *
      16                 :             :  * You should have received a copy of the GNU Lesser General Public
      17                 :             :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18                 :             :  *
      19                 :             :  * Author: Luca Bacci <luca.bacci@outlook.com>
      20                 :             :  */
      21                 :             : 
      22                 :             : #include "config.h"
      23                 :             : 
      24                 :             : #include <stdlib.h>
      25                 :             : #include <stdio.h>
      26                 :             : 
      27                 :             : #include "gtypes.h"
      28                 :             : #include "gunicodeprivate.h"
      29                 :             : #include "gprintprivate.h"
      30                 :             : #include "gprintfint.h"
      31                 :             : 
      32                 :             : #ifndef _WIN32
      33                 :             : #include <unistd.h>
      34                 :             : #else
      35                 :             : #include "gwin32private.h"
      36                 :             : #include <io.h>
      37                 :             : #endif
      38                 :             : 
      39                 :             : #define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \
      40                 :             :                             (wc == 0x7f) || \
      41                 :             :                             (wc >= 0x80 && wc < 0xa0)))
      42                 :             : 
      43                 :             : char *
      44                 :           0 : g_print_convert (const char *string,
      45                 :             :                  const char *charset)
      46                 :             : {
      47                 :           0 :   if (!g_utf8_validate (string, -1, NULL))
      48                 :             :     {
      49                 :           0 :       GString *gstring = g_string_new ("[Invalid UTF-8] ");
      50                 :             :       guchar *p;
      51                 :             : 
      52                 :           0 :       for (p = (guchar *)string; *p; p++)
      53                 :             :         {
      54                 :           0 :           if (CHAR_IS_SAFE(*p) &&
      55                 :           0 :               !(*p == '\r' && *(p + 1) != '\n') &&
      56                 :           0 :               *p < 0x80)
      57                 :           0 :             g_string_append_c (gstring, *p);
      58                 :             :           else
      59                 :           0 :             g_string_append_printf (gstring, "\\x%02x", (guint)(guchar)*p);
      60                 :             :         }
      61                 :             : 
      62                 :           0 :       return g_string_free (gstring, FALSE);
      63                 :             :     }
      64                 :             :   else
      65                 :             :     {
      66                 :           0 :       GError *err = NULL;
      67                 :             : 
      68                 :           0 :       gchar *result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, &err);
      69                 :           0 :       if (result)
      70                 :           0 :         return result;
      71                 :             :       else
      72                 :             :         {
      73                 :             :           /* Not thread-safe, but doesn't matter if we print the warning twice
      74                 :             :            */
      75                 :             :           static gboolean warned = FALSE;
      76                 :           0 :           if (!warned)
      77                 :             :             {
      78                 :           0 :               warned = TRUE;
      79                 :           0 :               _g_fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message);
      80                 :             :             }
      81                 :           0 :           g_error_free (err);
      82                 :             : 
      83                 :           0 :           return g_strdup (string);
      84                 :             :         }
      85                 :             :     }
      86                 :             : }
      87                 :             : 
      88                 :             : #ifdef _WIN32
      89                 :             : 
      90                 :             : static int
      91                 :             : print_console_nolock (const char *string,
      92                 :             :                       FILE       *stream)
      93                 :             : {
      94                 :             :   HANDLE handle = (HANDLE) _get_osfhandle (_fileno (stream));
      95                 :             :   size_t size = strlen (string);
      96                 :             :   DWORD written = 0;
      97                 :             : 
      98                 :             :   if (size > INT_MAX)
      99                 :             :     return 0;
     100                 :             : 
     101                 :             :   /* WriteFile are WriteConsole are limited to DWORD lengths,
     102                 :             :    * but int and DWORD should are of the same size, so we don't
     103                 :             :    * care.
     104                 :             :    */
     105                 :             :   G_STATIC_ASSERT (INT_MAX <= MAXDWORD);
     106                 :             : 
     107                 :             :   /* We might also check if the source string is ASCII */
     108                 :             :   if (GetConsoleOutputCP () == CP_UTF8)
     109                 :             :     {
     110                 :             :       /* If the output codepage is UTF-8, we can just call WriteFile,
     111                 :             :        * avoiding a conversion to UTF-16 (which probably will be done
     112                 :             :        * by ConDrv).
     113                 :             :        */
     114                 :             :       /* Note: we cannot use fputs() here. When outputting to the
     115                 :             :        * console, the UCRT converts the passed string to the console
     116                 :             :        * charset, which is UTF-8, but interprets the string in the
     117                 :             :        * LC_CTYPE charset, which can be anything.
     118                 :             :        */
     119                 :             : 
     120                 :             :       if (!WriteFile (handle, string, size, &written, NULL))
     121                 :             :         WIN32_API_FAILED ("WriteFile");
     122                 :             :     }
     123                 :             :   else
     124                 :             :     {
     125                 :             :       /* Convert to UTF-16 and output using WriteConsole */
     126                 :             : 
     127                 :             :       /* Note: we can't use fputws() with mode _O_U16TEXT because:
     128                 :             :        *
     129                 :             :        * - file descriptors cannot be locked, unlike FILE streams, so
     130                 :             :        *   we cannot set a custom mode on the file descriptor.
     131                 :             :        * - the fputws() implementation is not very good: it outputs codeunit
     132                 :             :        *   by codeunit in a loop, so it's slow [1] and breaks UTF-16 surrogate
     133                 :             :        *   pairs [2].
     134                 :             :        *
     135                 :             :        * [1] https://github.com/microsoft/terminal/issues/18124#issuecomment-2451987873
     136                 :             :        * [2] https://developercommunity.visualstudio.com/t/wprintf-with-_setmode-_O_U16TEXT-or-_O_U/10447076
     137                 :             :        */
     138                 :             : 
     139                 :             :       wchar_t buffer[1024];
     140                 :             :       wchar_t *utf16 = NULL;
     141                 :             :       size_t utf16_len = 0;
     142                 :             :       DWORD utf16_written = 0;
     143                 :             : 
     144                 :             :       g_utf8_to_utf16_make_valid (string,
     145                 :             :                                   buffer, G_N_ELEMENTS (buffer),
     146                 :             :                                   &utf16, &utf16_len);
     147                 :             : 
     148                 :             :       /* The length of the UTF-16 string (in count of gunichar2) cannot be
     149                 :             :        * greater than the length of the UTF-8 string (in count of bytes).
     150                 :             :        * So utf16_len <= size <= INT_MAX <= MAXDWORD.
     151                 :             :        */
     152                 :             :       g_assert (utf16_len <= size);
     153                 :             : 
     154                 :             :       if (!WriteConsole (handle, utf16, utf16_len, &utf16_written, NULL))
     155                 :             :         WIN32_API_FAILED ("WriteConsole");
     156                 :             : 
     157                 :             :       if (utf16_written < utf16_len)
     158                 :             :         {
     159                 :             :           written = g_utf8_to_utf16_make_valid_backtrack (string, utf16_written);
     160                 :             :         }
     161                 :             :       else
     162                 :             :         {
     163                 :             :           written = size;
     164                 :             :         }
     165                 :             : 
     166                 :             :       if (utf16 != buffer)
     167                 :             :         g_free (utf16);
     168                 :             :     }
     169                 :             : 
     170                 :             :   if (written > INT_MAX)
     171                 :             :     written = INT_MAX;
     172                 :             : 
     173                 :             :   return (int) written;
     174                 :             : }
     175                 :             : 
     176                 :             : static int
     177                 :             : print_console (const char *string,
     178                 :             :                FILE       *stream)
     179                 :             : {
     180                 :             :   int ret;
     181                 :             : 
     182                 :             :   /* Locking the stream is not important, but leads
     183                 :             :    * to nicer output in case of concurrent writes.
     184                 :             :    */
     185                 :             :   _lock_file (stream);
     186                 :             : 
     187                 :             : #if defined (_MSC_VER) || defined (_UCRT)
     188                 :             :   _fflush_nolock (stream);
     189                 :             : #else
     190                 :             :   fflush (stream);
     191                 :             : #endif
     192                 :             : 
     193                 :             :   ret = print_console_nolock (string, stream);
     194                 :             : 
     195                 :             :   _unlock_file (stream);
     196                 :             : 
     197                 :             :   return ret;
     198                 :             : }
     199                 :             : 
     200                 :             : #endif /* _WIN32 */
     201                 :             : 
     202                 :             : static int
     203                 :       30232 : print_string (char const *string,
     204                 :             :               FILE       *stream)
     205                 :             : {
     206                 :       30232 :   size_t written = fwrite (string, 1, strlen (string), stream);
     207                 :             : 
     208                 :       30232 :   return MIN (written, INT_MAX);
     209                 :             : }
     210                 :             : 
     211                 :             : int
     212                 :       30232 : g_fputs (char const *string,
     213                 :             :          FILE       *stream)
     214                 :             : {
     215                 :             :   int ret;
     216                 :             : 
     217                 :             : #ifdef _WIN32
     218                 :             :   if (g_win32_file_stream_is_console_output (stream))
     219                 :             :     {
     220                 :             :       ret = print_console (string, stream);
     221                 :             : 
     222                 :             :       if (string[ret] != '\0')
     223                 :             :         ret += print_string (&string[ret], stream);
     224                 :             :     }
     225                 :             :   else
     226                 :             :     {
     227                 :             :       ret = print_string (string, stream);
     228                 :             :     }
     229                 :             : #else
     230                 :             :   const char *charset;
     231                 :             : 
     232                 :       30232 :   if (isatty (fileno (stream)) &&
     233                 :           0 :       !g_get_charset (&charset))
     234                 :           0 :     {
     235                 :           0 :       char *converted = g_print_convert (string, charset);
     236                 :           0 :       ret = print_string (converted, stream);
     237                 :           0 :       g_free (converted);
     238                 :             :     }
     239                 :             :   else
     240                 :             :     {
     241                 :       30232 :       ret = print_string (string, stream);
     242                 :             :     }
     243                 :             : #endif
     244                 :             : 
     245                 :       30232 :   return ret;
     246                 :             : }
        

Generated by: LCOV version 2.0-1