Coverage Report

Created: 2026-09-17 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/log4cplus/src/timehelper.cxx
Line
Count
Source
1
// Module:  Log4CPLUS
2
// File:    timehelper.cxx
3
// Created: 4/2003
4
// Author:  Tad E. Smith
5
//
6
//
7
// Copyright 2003-2017 Tad E. Smith
8
//
9
// Licensed under the Apache License, Version 2.0 (the "License");
10
// you may not use this file except in compliance with the License.
11
// You may obtain a copy of the License at
12
//
13
//     http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software
16
// distributed under the License is distributed on an "AS IS" BASIS,
17
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
// See the License for the specific language governing permissions and
19
// limitations under the License.
20
21
#include <log4cplus/helpers/timehelper.h>
22
#include <log4cplus/helpers/loglog.h>
23
#include <log4cplus/streams.h>
24
#include <log4cplus/helpers/stringhelper.h>
25
#include <log4cplus/internal/internal.h>
26
27
#include <algorithm>
28
#include <vector>
29
#include <iomanip>
30
#include <cassert>
31
#include <cerrno>
32
#if defined (UNICODE)
33
#include <cwchar>
34
#endif
35
36
#if defined (LOG4CPLUS_HAVE_SYS_TYPES_H)
37
#include <sys/types.h>
38
#endif
39
40
#if defined(LOG4CPLUS_HAVE_SYS_TIME_H)
41
#include <sys/time.h>
42
#endif
43
44
#if defined (LOG4CPLUS_HAVE_SYS_TIMEB_H)
45
#include <sys/timeb.h>
46
#endif
47
48
#if defined(LOG4CPLUS_HAVE_GMTIME_R) && !defined(LOG4CPLUS_SINGLE_THREADED)
49
#define LOG4CPLUS_NEED_GMTIME_R
50
#endif
51
52
#if defined(LOG4CPLUS_HAVE_LOCALTIME_R) && !defined(LOG4CPLUS_SINGLE_THREADED)
53
#define LOG4CPLUS_NEED_LOCALTIME_R
54
#endif
55
56
#include <log4cplus/config/windowsh-inc.h>
57
58
59
namespace log4cplus { namespace helpers {
60
61
const int ONE_SEC_IN_USEC = 1000000;
62
63
using std::mktime;
64
using std::gmtime;
65
using std::localtime;
66
#if defined (UNICODE)
67
using std::wcsftime;
68
#else
69
using std::strftime;
70
#endif
71
72
73
Time
74
from_struct_tm (tm * t)
75
0
{
76
0
    time_t time = helpers::mktime(t);
77
0
    if (LOG4CPLUS_LIKELY (time != -1))
78
0
        return from_time_t (time);
79
0
    else
80
0
    {
81
0
        int eno = errno;
82
0
        throw std::system_error (eno, std::system_category (),
83
0
            "from_struct_tm(): mktime() failed");
84
0
    }
85
0
}
86
87
void
88
gmTime (tm* t, Time const & the_time)
89
0
{
90
0
    time_t clock = to_time_t (the_time);
91
#if defined (LOG4CPLUS_HAVE_GMTIME_S) && defined (_MSC_VER)
92
    errno_t eno;
93
    if (LOG4CPLUS_UNLIKELY ((eno = gmtime_s (t, &clock)) != 0))
94
        throw std::system_error (eno, std::system_category (),
95
            "gmTime(): gmtime_s() failed");
96
#elif defined (LOG4CPLUS_HAVE_GMTIME_S) && defined (__BORLANDC__)
97
    gmtime_s (&clock, t);
98
#elif defined (LOG4CPLUS_NEED_GMTIME_R)
99
    gmtime_r (&clock, t);
100
#else
101
    tm* tmp = helpers::gmtime(&clock);
102
    *t = *tmp;
103
#endif
104
0
}
105
106
107
void
108
localTime (tm* t, Time const & the_time)
109
1.09M
{
110
1.09M
    time_t clock = to_time_t (the_time);
111
1.09M
#ifdef LOG4CPLUS_NEED_LOCALTIME_R
112
1.09M
    ::localtime_r(&clock, t);
113
#elif defined (LOG4CPLUS_HAVE_LOCALTIME_S)
114
    errno_t eno;
115
    if (LOG4CPLUS_UNLIKELY ((eno = localtime_s (t, &clock)) != 0))
116
        throw std::system_error (eno, std::system_category (),
117
            "localTime(): localtime_s() failed");
118
#else
119
    tm* tmp = helpers::localtime(&clock);
120
    *t = *tmp;
121
#endif
122
1.09M
}
123
124
125
namespace
126
{
127
128
129
static log4cplus::tstring const padding_zeros[4] =
130
{
131
    { LOG4CPLUS_TEXT("000") },
132
    { LOG4CPLUS_TEXT("00") },
133
    { LOG4CPLUS_TEXT("0") },
134
    { }
135
};
136
137
138
static log4cplus::tstring const uc_q_padding_zeros[4] =
139
{
140
    { LOG4CPLUS_TEXT(".000") },
141
    { LOG4CPLUS_TEXT(".00") },
142
    { LOG4CPLUS_TEXT(".0") },
143
    { LOG4CPLUS_TEXT(".") }
144
};
145
146
147
static
148
void
149
build_q_value (log4cplus::tstring & q_str, long tv_usec)
150
1.09M
{
151
1.09M
    convertIntegerToString(q_str, tv_usec / 1000);
152
1.09M
    std::size_t const len = q_str.length();
153
1.09M
    if (len <= 2)
154
111k
        q_str.insert (0, padding_zeros[q_str.length()]);
155
1.09M
}
156
157
158
static
159
void
160
build_uc_q_value (log4cplus::tstring & uc_q_str, long tv_usec,
161
    log4cplus::tstring & tmp)
162
0
{
163
0
    build_q_value (uc_q_str, tv_usec);
164
165
0
    convertIntegerToString(tmp, tv_usec % 1000);
166
0
    std::size_t const usecs_len = tmp.length();
167
0
    tmp.insert (0, usecs_len <= 3
168
0
        ? uc_q_padding_zeros[usecs_len] : uc_q_padding_zeros[3]);
169
0
    uc_q_str.append (tmp);
170
0
}
171
172
173
} // namespace
174
175
176
log4cplus::tstring
177
getFormattedTime(const log4cplus::tstring& fmt_orig,
178
    Time const & the_time, bool use_gmtime)
179
1.09M
{
180
1.09M
    if (fmt_orig.empty () || fmt_orig[0] == 0)
181
0
        return log4cplus::tstring ();
182
183
1.09M
    tm time;
184
185
1.09M
    if (use_gmtime)
186
0
        gmTime (&time, the_time);
187
1.09M
    else
188
1.09M
        localTime (&time, the_time);
189
190
1.09M
    enum State
191
1.09M
    {
192
1.09M
        TEXT,
193
1.09M
        PERCENT_SIGN
194
1.09M
    };
195
196
1.09M
    internal::gft_scratch_pad & gft_sp = internal::get_gft_scratch_pad ();
197
1.09M
    gft_sp.reset ();
198
199
1.09M
    std::size_t const fmt_orig_size = gft_sp.fmt.size ();
200
1.09M
    gft_sp.ret.reserve (fmt_orig_size + fmt_orig_size / 3);
201
1.09M
    State state = TEXT;
202
203
    // Walk the format string and process all occurences of %q, %Q and %s.
204
205
1.09M
    long const tv_usec = microseconds_part (the_time);
206
1.09M
    time_t const tv_sec = to_time_t (the_time);
207
1.09M
    for (auto fmt_ch : fmt_orig)
208
21.9M
    {
209
21.9M
        switch (state)
210
21.9M
        {
211
14.2M
        case TEXT:
212
14.2M
        {
213
14.2M
            if (fmt_ch == LOG4CPLUS_TEXT ('%'))
214
7.66M
                state = PERCENT_SIGN;
215
6.57M
            else
216
6.57M
                gft_sp.ret.push_back (fmt_ch);
217
14.2M
        }
218
14.2M
        break;
219
220
7.66M
        case PERCENT_SIGN:
221
7.66M
        {
222
7.66M
            switch (fmt_ch)
223
7.66M
            {
224
1.09M
            case LOG4CPLUS_TEXT ('q'):
225
1.09M
            {
226
1.09M
                if (! gft_sp.q_str_valid)
227
1.09M
                {
228
1.09M
                    build_q_value (gft_sp.q_str, tv_usec);
229
1.09M
                    gft_sp.q_str_valid = true;
230
1.09M
                }
231
1.09M
                gft_sp.ret.append (gft_sp.q_str);
232
1.09M
                state = TEXT;
233
1.09M
            }
234
1.09M
            break;
235
236
0
            case LOG4CPLUS_TEXT ('Q'):
237
0
            {
238
0
                if (! gft_sp.uc_q_str_valid)
239
0
                {
240
0
                    build_uc_q_value (gft_sp.uc_q_str, tv_usec, gft_sp.tmp);
241
0
                    gft_sp.uc_q_str_valid = true;
242
0
                }
243
0
                gft_sp.ret.append (gft_sp.uc_q_str);
244
0
                state = TEXT;
245
0
            }
246
0
            break;
247
248
            // Windows do not support %s format specifier
249
            // (seconds since epoch).
250
0
            case LOG4CPLUS_TEXT ('s'):
251
0
            {
252
0
                if (! gft_sp.s_str_valid)
253
0
                {
254
0
                    convertIntegerToString (gft_sp.s_str, tv_sec);
255
0
                    gft_sp.s_str_valid = true;
256
0
                }
257
0
                gft_sp.ret.append (gft_sp.s_str);
258
0
                state = TEXT;
259
0
            }
260
0
            break;
261
262
6.57M
            default:
263
6.57M
            {
264
6.57M
                gft_sp.ret.push_back (LOG4CPLUS_TEXT ('%'));
265
6.57M
                gft_sp.ret.push_back (fmt_ch);
266
6.57M
                state = TEXT;
267
6.57M
            }
268
7.66M
            }
269
7.66M
        }
270
7.66M
        break;
271
21.9M
        }
272
21.9M
    }
273
274
    // Finally call strftime/wcsftime to format the rest of the string.
275
276
1.09M
    gft_sp.fmt.swap (gft_sp.ret);
277
1.09M
    std::size_t buffer_size = gft_sp.fmt.size () + 1;
278
1.09M
    std::size_t len;
279
280
    // Limit how far can the buffer grow. This is necessary so that we
281
    // catch bad format string. Some implementations of strftime() signal
282
    // both too small buffer and invalid format string by returning 0
283
    // without changing errno.
284
1.09M
    std::size_t const buffer_size_max
285
1.09M
        = (std::max) (static_cast<std::size_t>(1024), buffer_size * 16);
286
    
287
1.09M
    buffer_size = (std::max) (buffer_size, gft_sp.buffer.capacity());
288
289
1.09M
    do
290
1.09M
    {
291
1.09M
        gft_sp.buffer.resize (buffer_size);
292
1.09M
        errno = 0;
293
#ifdef UNICODE
294
        len = helpers::wcsftime(&gft_sp.buffer[0], buffer_size,
295
            gft_sp.fmt.c_str(), &time);
296
#else
297
1.09M
        len = helpers::strftime(&gft_sp.buffer[0], buffer_size,
298
1.09M
            gft_sp.fmt.c_str(), &time);
299
1.09M
#endif
300
1.09M
        if (len == 0)
301
25
        {
302
25
            int const eno = errno;
303
25
            buffer_size *= 2;
304
25
            if (buffer_size > buffer_size_max)
305
0
            {
306
0
                LogLog::getLogLog ()->error (
307
0
                    LOG4CPLUS_TEXT("Error in strftime(): ")
308
0
                    + convertIntegerToString (eno), true);
309
0
            }
310
25
        }
311
1.09M
    }
312
1.09M
    while (len == 0);
313
314
1.09M
    return tstring (gft_sp.buffer.begin (), gft_sp.buffer.begin () + len);
315
1.09M
}
316
317
318
} } // namespace log4cplus { namespace helpers {