1//
2// Copyright 2020 Serge Martin
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20// OTHER DEALINGS IN THE SOFTWARE.
21//
22// Extract from Serge's printf clover code by airlied.
23
24#include "u_printf.h"
25#include <assert.h>
26#include <stdarg.h>
27#include "util/macros.h"
28
29/* Some versions of MinGW are missing _vscprintf's declaration, although they
30 * still provide the symbol in the import library. */
31#ifdef __MINGW32__
32_CRTIMP int _vscprintf(const char *format, va_list argptr);
33#endif
34
35#ifndef va_copy
36#ifdef __va_copy
37#define va_copy(dest, src) __va_copy((dest), (src))
38#else
39#define va_copy(dest, src) (dest) = (src)
40#endif
41#endif
42
43size_t util_printf_next_spec_pos(const std::string &s, size_t pos)
44{
45   size_t next_tok, spec_pos;
46   do {
47      pos = s.find_first_of('%', pos);
48
49      if (pos == std::string::npos)
50         return -1;
51
52      if (s[pos + 1] == '%') {
53         pos += 2;
54         continue;
55      }
56
57      next_tok = s.find_first_of('%', pos + 1);
58      spec_pos = s.find_first_of("cdieEfFgGaAosuxXp", pos + 1);
59      if (spec_pos != std::string::npos)
60         if (spec_pos < next_tok)
61            return spec_pos;
62
63      pos++;
64   } while (1);
65}
66
67size_t util_printf_next_spec_pos(const char *str, size_t pos)
68{
69   return util_printf_next_spec_pos(std::string(str), pos);
70}
71
72size_t
73u_printf_length(const char *fmt, va_list untouched_args)
74{
75   int size;
76   char junk;
77
78   /* Make a copy of the va_list so the original caller can still use it */
79   va_list args;
80   va_copy(args, untouched_args);
81
82#ifdef _WIN32
83   /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
84    * if the number of characters to write is greater than count.
85    */
86   size = _vscprintf(fmt, args);
87   (void)junk;
88#else
89   size = vsnprintf(&junk, 1, fmt, args);
90#endif
91   assert(size >= 0);
92
93   va_end(args);
94
95   return size;
96}
97