1 /* Log file output.
2 Copyright (C) 2003, 2005, 2009, 2021, 2023 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /* Written by Bruno Haible <bruno@clisp.org>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification. */
24 #include "gettextP.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /* Handle multi-threaded applications. */
31 #ifdef _LIBC
32 # include <bits/libc-lock.h>
33 #else
34 # include "glthread/lock.h"
35 #endif
36
37 /* Separator between msgctxt and msgid in .mo files. */
38 #define MSGCTXT_SEPARATOR '\004' /* EOT */
39
40 /* Print an ASCII string with quotes and escape sequences where needed. */
41 static void
42 print_escaped (FILE *stream, const char *str, const char *str_end)
43 {
44 putc ('"', stream);
45 for (; str != str_end; str++)
46 if (*str == '\n')
47 {
48 fputs ("\\n\"", stream);
49 if (str + 1 == str_end)
50 return;
51 fputs ("\n\"", stream);
52 }
53 else
54 {
55 if (*str == '"' || *str == '\\')
56 putc ('\\', stream);
57 putc (*str, stream);
58 }
59 putc ('"', stream);
60 }
61
62 static char *last_logfilename = NULL;
63 static FILE *last_logfile = NULL;
64 __libc_lock_define_initialized (static, lock)
65
66 static inline void
67 _nl_log_untranslated_locked (const char *logfilename, const char *domainname,
68 const char *msgid1, const char *msgid2, int plural)
69 {
70 FILE *logfile;
71 const char *separator;
72
73 /* Can we reuse the last opened logfile? */
74 if (last_logfilename == NULL || strcmp (logfilename, last_logfilename) != 0)
75 {
76 /* Close the last used logfile. */
77 if (last_logfilename != NULL)
78 {
79 if (last_logfile != NULL)
80 {
81 fclose (last_logfile);
82 last_logfile = NULL;
83 }
84 free (last_logfilename);
85 last_logfilename = NULL;
86 }
87 /* Open the logfile. */
88 last_logfilename = (char *) malloc (strlen (logfilename) + 1);
89 if (last_logfilename == NULL)
90 return;
91 strcpy (last_logfilename, logfilename);
92 last_logfile = fopen (logfilename, "a");
93 if (last_logfile == NULL)
94 return;
95 }
96 logfile = last_logfile;
97
98 fprintf (logfile, "domain ");
99 print_escaped (logfile, domainname, domainname + strlen (domainname));
100 separator = strchr (msgid1, MSGCTXT_SEPARATOR);
101 if (separator != NULL)
102 {
103 /* The part before the MSGCTXT_SEPARATOR is the msgctxt. */
104 fprintf (logfile, "\nmsgctxt ");
105 print_escaped (logfile, msgid1, separator);
106 msgid1 = separator + 1;
107 }
108 fprintf (logfile, "\nmsgid ");
109 print_escaped (logfile, msgid1, msgid1 + strlen (msgid1));
110 if (plural)
111 {
112 fprintf (logfile, "\nmsgid_plural ");
113 print_escaped (logfile, msgid2, msgid2 + strlen (msgid2));
114 fprintf (logfile, "\nmsgstr[0] \"\"\n");
115 }
116 else
117 fprintf (logfile, "\nmsgstr \"\"\n");
118 putc ('\n', logfile);
119 }
120
121 /* Add to the log file an entry denoting a failed translation. */
122 void
123 _nl_log_untranslated (const char *logfilename, const char *domainname,
124 const char *msgid1, const char *msgid2, int plural)
125 {
126 __libc_lock_lock (lock);
127 _nl_log_untranslated_locked (logfilename, domainname, msgid1, msgid2, plural);
128 __libc_lock_unlock (lock);
129 }