Coverage Report

Created: 2025-08-26 06:48

/src/logging-log4cxx/src/main/cpp/patternparser.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/logstring.h>
19
#include <log4cxx/pattern/patternparser.h>
20
#include <log4cxx/pattern/literalpatternconverter.h>
21
#include <log4cxx/helpers/loglog.h>
22
23
using namespace LOG4CXX_NS;
24
using namespace LOG4CXX_NS::pattern;
25
using namespace LOG4CXX_NS::helpers;
26
27
const logchar PatternParser::ESCAPE_CHAR = 0x25; // '%'
28
29
30
/**
31
 * Private constructor.
32
 */
33
PatternParser::PatternParser()
34
0
{
35
0
}
36
37
bool PatternParser::isUnicodeIdentifierStart(logchar ch)
38
435k
{
39
  //
40
  //   greatly simplified version checks if
41
  //     character is USACII alpha or number
42
  //
43
435k
  return (ch >= 0x41 /* 'A' */ && ch <= 0x5A /* 'Z' */) ||
44
435k
    (ch >= 0x61 /* 'a' */ && ch <= 0x7A /* 'z' */) ||
45
435k
    (ch >= 0x30 /* '0' */ && ch <= 0x39 /* '9' */);
46
435k
}
47
48
bool PatternParser::isUnicodeIdentifierPart(logchar ch)
49
344k
{
50
  //
51
  //   greatly simplified version checks if
52
  //     character is USACII alpha or number
53
  //
54
344k
  return isUnicodeIdentifierStart(ch)
55
344k
    || (ch == 0x5F /* '_' */);
56
344k
}
57
58
size_t PatternParser::extractConverter(
59
  logchar lastChar, const LogString& pattern,
60
  LogString::size_type i, LogString& convBuf,
61
  LogString& currentLiteral)
62
91.1k
{
63
91.1k
  if (!convBuf.empty())
64
0
  {
65
0
    convBuf.erase(convBuf.begin(), convBuf.end());
66
0
  }
67
68
  // When this method is called, lastChar points to the first character of the
69
  // conversion word. For example:
70
  // For "%hello"     lastChar = 'h'
71
  // For "%-5hello"   lastChar = 'h'
72
  //System.out.println("lastchar is "+lastChar);
73
91.1k
  if (!isUnicodeIdentifierStart(lastChar))
74
5.09k
  {
75
5.09k
    return i;
76
5.09k
  }
77
78
86.0k
  convBuf.append(1, lastChar);
79
80
86.0k
  while (
81
345k
    (i < pattern.length())
82
345k
    && isUnicodeIdentifierPart(pattern[i]))
83
259k
  {
84
259k
    convBuf.append(1, pattern[i]);
85
259k
    currentLiteral.append(1, pattern[i]);
86
87
    //System.out.println("conv buffer is now ["+convBuf+"].");
88
259k
    i++;
89
259k
  }
90
91
86.0k
  return i;
92
91.1k
}
93
94
95
size_t PatternParser::extractOptions(const LogString& pattern, LogString::size_type i,
96
  std::vector<LogString>& options)
97
86.0k
{
98
141k
  while ((i < pattern.length()) && (pattern[i] == 0x7B /* '{' */))
99
55.7k
  {
100
55.7k
    size_t end = pattern.find(0x7D /* '}' */, i);
101
102
55.7k
    if (end == pattern.npos)
103
422
    {
104
422
      break;
105
422
    }
106
107
55.3k
    LogString r(pattern.substr(i + 1, end - i - 1));
108
55.3k
    options.push_back(r);
109
55.3k
    i = end + 1;
110
55.3k
  }
111
112
86.0k
  return i;
113
86.0k
}
114
115
void PatternParser::parse(
116
  const LogString& pattern,
117
  std::vector<PatternConverterPtr>& patternConverters,
118
  std::vector<FormattingInfoPtr>& formattingInfos,
119
  const PatternMap& rules)
120
3.57k
{
121
122
3.57k
  LogString currentLiteral;
123
124
3.57k
  size_t patternLength = pattern.length();
125
3.57k
  int state = LITERAL_STATE;
126
3.57k
  logchar c;
127
3.57k
  size_t i = 0;
128
3.57k
  int minDigitCount{ 0 }, maxDigitCount{ 0 };
129
3.57k
  FormattingInfoPtr formattingInfo(FormattingInfo::getDefault());
130
131
616k
  while (i < patternLength)
132
613k
  {
133
613k
    c = pattern[i++];
134
135
613k
    switch (state)
136
613k
    {
137
470k
      case LITERAL_STATE:
138
139
        // In literal state, the last char is always a literal.
140
470k
        if (i == patternLength)
141
464
        {
142
464
          currentLiteral.append(1, c);
143
144
464
          continue;
145
464
        }
146
147
470k
        if (c == ESCAPE_CHAR)
148
93.4k
        {
149
          // peek at the next char.
150
93.4k
          if (pattern[i] == ESCAPE_CHAR)
151
1.09k
          {
152
1.09k
            currentLiteral.append(1, c);
153
1.09k
            i++; // move pointer
154
1.09k
          }
155
92.3k
          else
156
92.3k
          {
157
92.3k
            if (!currentLiteral.empty())
158
25.7k
            {
159
25.7k
              patternConverters.push_back(
160
25.7k
                LiteralPatternConverter::newInstance(currentLiteral));
161
25.7k
              formattingInfos.push_back(FormattingInfo::getDefault());
162
25.7k
              currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
163
25.7k
            }
164
165
92.3k
            currentLiteral.append(1, c); // append %
166
92.3k
            state = CONVERTER_STATE;
167
92.3k
            formattingInfo = FormattingInfo::getDefault();
168
92.3k
          }
169
93.4k
        }
170
376k
        else
171
376k
        {
172
376k
          currentLiteral.append(1, c);
173
376k
        }
174
175
470k
        break;
176
177
134k
      case CONVERTER_STATE:
178
134k
        currentLiteral.append(1, c);
179
180
134k
        switch (c)
181
134k
        {
182
41.6k
          case 0x2D: // '-'
183
41.6k
            formattingInfo = std::make_shared<FormattingInfo>(
184
41.6k
                  true, formattingInfo->getMinLength(),
185
41.6k
                  formattingInfo->getMaxLength());
186
187
41.6k
            break;
188
189
1.81k
          case 0x2E: // '.'
190
1.81k
            state = DOT_STATE;
191
192
1.81k
            break;
193
194
90.5k
          default:
195
196
90.5k
            if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
197
2.30k
            {
198
2.30k
              formattingInfo = std::make_shared<FormattingInfo>(
199
2.30k
                    formattingInfo->isLeftAligned(), c - 0x30 /* '0' */,
200
2.30k
                    formattingInfo->getMaxLength());
201
2.30k
              state = MIN_STATE;
202
2.30k
              minDigitCount = 1;
203
2.30k
            }
204
88.2k
            else
205
88.2k
            {
206
88.2k
              i = finalizeConverter(
207
88.2k
                  c, pattern, i, currentLiteral, formattingInfo,
208
88.2k
                  rules, patternConverters, formattingInfos);
209
210
              // Next pattern is assumed to be a literal.
211
88.2k
              state = LITERAL_STATE;
212
88.2k
              formattingInfo = FormattingInfo::getDefault();
213
214
88.2k
              if (!currentLiteral.empty())
215
0
              {
216
0
                currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
217
0
              }
218
88.2k
            }
219
134k
        } // switch
220
221
134k
        break;
222
223
134k
      case MIN_STATE:
224
3.49k
        currentLiteral.append(1, c);
225
226
3.49k
        if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */) && minDigitCount < 3)
227
1.23k
        {
228
1.23k
          formattingInfo = std::make_shared<FormattingInfo>(
229
1.23k
                formattingInfo->isLeftAligned(),
230
1.23k
                (formattingInfo->getMinLength() * 10) + (c - 0x30 /* '0' */),
231
1.23k
                formattingInfo->getMaxLength());
232
1.23k
          ++minDigitCount;
233
1.23k
        }
234
2.25k
        else if (c == 0x2E /* '.' */)
235
610
        {
236
610
          state = DOT_STATE;
237
610
        }
238
1.64k
        else
239
1.64k
        {
240
1.64k
          i = finalizeConverter(
241
1.64k
              c, pattern, i, currentLiteral, formattingInfo,
242
1.64k
              rules, patternConverters, formattingInfos);
243
1.64k
          state = LITERAL_STATE;
244
1.64k
          formattingInfo = FormattingInfo::getDefault();
245
246
1.64k
          if (!currentLiteral.empty())
247
0
          {
248
0
            currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
249
0
          }
250
1.64k
        }
251
252
3.49k
        break;
253
254
2.41k
      case DOT_STATE:
255
2.41k
        currentLiteral.append(1, c);
256
257
2.41k
        if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */))
258
1.34k
        {
259
1.34k
          formattingInfo = std::make_shared<FormattingInfo>(
260
1.34k
                formattingInfo->isLeftAligned(), formattingInfo->getMinLength(),
261
1.34k
                c - 0x30 /* '0' */);
262
1.34k
          state = MAX_STATE;
263
1.34k
          maxDigitCount = 1;
264
1.34k
        }
265
1.06k
        else
266
1.06k
        {
267
1.06k
          LogLog::error(LOG4CXX_STR("Error in pattern, was expecting digit."));
268
269
1.06k
          state = LITERAL_STATE;
270
1.06k
        }
271
272
2.41k
        break;
273
274
2.20k
      case MAX_STATE:
275
2.20k
        currentLiteral.append(1, c);
276
277
2.20k
        if ((c >= 0x30 /* '0' */) && (c <= 0x39 /* '9' */) && maxDigitCount < 3)
278
895
        {
279
895
          formattingInfo = std::make_shared<FormattingInfo>(
280
895
                formattingInfo->isLeftAligned(), formattingInfo->getMinLength(),
281
895
                (formattingInfo->getMaxLength() * 10) + (c - 0x30 /* '0' */));
282
895
          ++maxDigitCount;
283
895
        }
284
1.30k
        else
285
1.30k
        {
286
1.30k
          i = finalizeConverter(
287
1.30k
              c, pattern, i, currentLiteral, formattingInfo,
288
1.30k
              rules, patternConverters, formattingInfos);
289
1.30k
          state = LITERAL_STATE;
290
1.30k
          formattingInfo = FormattingInfo::getDefault();
291
292
1.30k
          if (!currentLiteral.empty())
293
0
          {
294
0
            currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
295
0
          }
296
1.30k
        }
297
298
2.20k
        break;
299
613k
    } // switch
300
613k
  }
301
302
  // while
303
3.57k
  if (currentLiteral.length() != 0)
304
722
  {
305
722
    patternConverters.push_back(
306
722
      LiteralPatternConverter::newInstance(currentLiteral));
307
722
    formattingInfos.push_back(FormattingInfo::getDefault());
308
722
  }
309
3.57k
}
310
311
312
PatternConverterPtr PatternParser::createConverter(
313
  const LogString& converterId,
314
  LogString& currentLiteral,
315
  const PatternMap& rules,
316
  std::vector<LogString>& options)
317
86.0k
{
318
319
86.0k
  LogString converterName(converterId);
320
321
348k
  for (size_t i = converterId.length(); i > 0; i--)
322
344k
  {
323
344k
    converterName = converterName.substr(0, i);
324
344k
    PatternMap::const_iterator iter = rules.find(converterName);
325
326
344k
    if (iter != rules.end())
327
82.6k
    {
328
82.6k
      currentLiteral.erase(currentLiteral.begin(),
329
82.6k
        currentLiteral.end() - (converterId.length() - i));
330
82.6k
      return (iter->second)(options);
331
82.6k
    }
332
344k
  }
333
334
3.46k
  LogLog::error(LogString(LOG4CXX_STR("Unrecognized format specifier ")) + converterId);
335
336
3.46k
  return PatternConverterPtr();
337
86.0k
}
338
339
size_t PatternParser::finalizeConverter(
340
  logchar c, const LogString& pattern, size_t i,
341
  LogString& currentLiteral, const FormattingInfoPtr& formattingInfo,
342
  const PatternMap&  rules,
343
  std::vector<PatternConverterPtr>& patternConverters,
344
  std::vector<FormattingInfoPtr>&  formattingInfos)
345
91.1k
{
346
91.1k
  LogString convBuf;
347
91.1k
  i = extractConverter(c, pattern, i, convBuf, currentLiteral);
348
349
91.1k
  if (convBuf.empty())
350
5.09k
  {
351
5.09k
    LogLog::error(LOG4CXX_STR("Empty conversion specifier"));
352
5.09k
    patternConverters.push_back(
353
5.09k
      LiteralPatternConverter::newInstance(currentLiteral));
354
5.09k
    formattingInfos.push_back(FormattingInfo::getDefault());
355
5.09k
  }
356
86.0k
  else
357
86.0k
  {
358
86.0k
    LogString converterId(convBuf);
359
360
86.0k
    std::vector<LogString> options;
361
86.0k
    i = extractOptions(pattern, i, options);
362
363
86.0k
    PatternConverterPtr pc(
364
86.0k
      createConverter(
365
86.0k
        converterId, currentLiteral, rules, options));
366
367
86.0k
    if (pc == NULL)
368
3.46k
    {
369
3.46k
      LogString msg(LOG4CXX_STR("Unrecognized conversion specifier ["));
370
3.46k
      msg.append(converterId);
371
3.46k
      msg.append(LOG4CXX_STR("] in conversion pattern."));
372
3.46k
      LogLog::error(msg);
373
3.46k
      patternConverters.push_back(
374
3.46k
        LiteralPatternConverter::newInstance(currentLiteral));
375
3.46k
      formattingInfos.push_back(FormattingInfo::getDefault());
376
3.46k
    }
377
82.6k
    else
378
82.6k
    {
379
82.6k
      patternConverters.push_back(pc);
380
82.6k
      formattingInfos.push_back(formattingInfo);
381
382
82.6k
      if (currentLiteral.length() > 0)
383
20.7k
      {
384
20.7k
        patternConverters.push_back(
385
20.7k
          LiteralPatternConverter::newInstance(currentLiteral));
386
20.7k
        formattingInfos.push_back(FormattingInfo::getDefault());
387
20.7k
      }
388
82.6k
    }
389
86.0k
  }
390
391
91.1k
  if (!currentLiteral.empty())
392
29.3k
  {
393
29.3k
    currentLiteral.erase(currentLiteral.begin(), currentLiteral.end());
394
29.3k
  }
395
396
91.1k
  return i;
397
91.1k
}