summaryrefslogtreecommitdiffstats
path: root/utility.h
blob: e1d3b9c2b06018e7f7e32100918fc6a583083cf8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/*
 * utility.h
 *
 * Home page of code is: https://www.smartmontools.org
 *
 * Copyright (C) 2002-11 Bruce Allen
 * Copyright (C) 2008-23 Christian Franke
 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef UTILITY_H_
#define UTILITY_H_

#define UTILITY_H_CVSID "$Id: utility.h 5519 2023-07-24 15:57:54Z chrfranke $"

#include <float.h> // *DBL_MANT_DIG
#include <time.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <string>

#include <sys/types.h> // for regex.h (according to POSIX)
#ifdef WITH_CXX11_REGEX
#include <regex>
#else
#include <regex.h>
#endif

#ifndef __GNUC__
#define __attribute_format_printf(x, y)  /**/
#elif defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO
// Check format of __mingw_*printf() instead of MSVCRT.DLL:*printf()
#define __attribute_format_printf(x, y)  __attribute__((format (gnu_printf, x, y)))
#else
#define __attribute_format_printf(x, y)  __attribute__((format (printf, x, y)))
#endif

// Make version information string
// lines: 1: version only, 2: version+copyright, >=3: full information
std::string format_version_info(const char * prog_name, int lines = 2);

// return (v)sprintf() formatted std::string
std::string strprintf(const char * fmt, ...)
    __attribute_format_printf(1, 2);
std::string vstrprintf(const char * fmt, va_list ap);

// Return true if STR starts with PREFIX
inline bool str_starts_with(const char * str, const char * prefix)
  { return !strncmp(str, prefix, strlen(prefix)); }

inline bool str_starts_with(const std::string & str, const char * prefix)
  { return !strncmp(str.c_str(), prefix, strlen(prefix)); }

// Convert time to broken-down local time, throw on error.
struct tm * time_to_tm_local(struct tm * tp, time_t t);

// Utility function prints date and time and timezone into a character
// buffer of length 64.  All the fuss is needed to get the
// right timezone info (sigh).
#define DATEANDEPOCHLEN 64
void dateandtimezoneepoch(char (& buffer)[DATEANDEPOCHLEN], time_t tval);

// like printf() except that we can control it better. Note --
// although the prototype is given here in utility.h, the function
// itself is defined differently in smartctl and smartd.  So the
// function definition(s) are in smartd.c and in smartctl.c.
void pout(const char *fmt, ...)  
    __attribute_format_printf(1, 2);

// replacement for perror() with redirected output.
void syserror(const char *message);

// Function for processing -t selective... option in smartctl
int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode);

// Compile time check of byte ordering
// (inline const function allows compiler to remove dead code)
inline bool isbigendian()
{
#ifdef WORDS_BIGENDIAN
  return true;
#else
  return false;
#endif
}

void swap2(char *location);
void swap4(char *location);
void swap8(char *location);
// Typesafe variants using overloading
inline void swapx(unsigned short * p)
  { swap2((char*)p); }
inline void swapx(unsigned int * p)
  { swap4((char*)p); }
inline void swapx(uint64_t * p)
  { swap8((char*)p); }

// Runtime check of ./configure result, throws on error.
void check_config();

// This value follows the peripheral device type value as defined in
// SCSI Primary Commands, ANSI INCITS 301:1997.  It is also used in
// the ATA standard for packet devices to define the device type.
const char *packetdevicetype(int type);

// returns true if any of the n bytes are nonzero, else zero.
bool nonempty(const void * data, int size);

// needed to fix glibc bug
void FixGlibcTimeZoneBug();

// Copy not null terminated char array to null terminated string.
// Replace non-ascii characters.  Remove leading and trailing blanks.
const char * format_char_array(char * str, int strsize, const char * chr, int chrsize);

// Version for fixed size buffers.
template<size_t STRSIZE, size_t CHRSIZE>
inline const char * format_char_array(char (& str)[STRSIZE], const char (& chr)[CHRSIZE])
  { return format_char_array(str, (int)STRSIZE, chr, (int)CHRSIZE); }

// Format integer with thousands separator
const char * format_with_thousands_sep(char * str, int strsize, uint64_t val,
                                       const char * thousands_sep = 0);

// Format capacity with SI prefixes
const char * format_capacity(char * str, int strsize, uint64_t val,
                             const char * decimal_point = 0);

// Wrapper class for a raw data buffer
class raw_buffer
{
public:
  explicit raw_buffer(unsigned sz, unsigned char val = 0)
    : m_data(new unsigned char[sz]),
      m_size(sz)
    { memset(m_data, val, m_size); }

  ~raw_buffer()
    { delete [] m_data; }

  unsigned size() const
    { return m_size; }

  unsigned char * data()
    { return m_data; }
  const unsigned char * data() const
    { return m_data; }

private:
  unsigned char * m_data;
  unsigned m_size;

  raw_buffer(const raw_buffer &);
  void operator=(const raw_buffer &);
};

/// Wrapper class for FILE *.
class stdio_file
{
public:
  explicit stdio_file(FILE * f = 0, bool owner = false)
    : m_file(f), m_owner(owner) { }

  stdio_file(const char * name, const char * mode)
    : m_file(fopen(name, mode)), m_owner(true) { }

  ~stdio_file()
    {
      if (m_file && m_owner)
        fclose(m_file);
    }

  bool open(const char * name, const char * mode)
    {
      if (m_file && m_owner)
        fclose(m_file);
      m_file = fopen(name, mode);
      m_owner = true;
      return !!m_file;
    }

  void open(FILE * f, bool owner = false)
    {
      if (m_file && m_owner)
        fclose(m_file);
      m_file = f;
      m_owner = owner;
    }

  bool close()
    {
      if (!m_file)
        return true;
      bool ok = !ferror(m_file);
      if (fclose(m_file))
        ok = false;
      m_file = 0;
      return ok;
    }

  operator FILE * ()
    { return m_file; }

  bool operator!() const
    { return !m_file; }

private:
  FILE * m_file;
  bool m_owner;

  stdio_file(const stdio_file &);
  void operator=(const stdio_file &);
};

/// Wrapper class for POSIX regex(3) or std::regex
/// Supports copy & assignment and is compatible with STL containers.
class regular_expression
{
public:
  // Construction & assignment
#ifdef WITH_CXX11_REGEX
  regular_expression() = default;

#else
  regular_expression();

  ~regular_expression();

  regular_expression(const regular_expression & x);

  regular_expression & operator=(const regular_expression & x);
#endif

  /// Construct with pattern, throw on error.
  explicit regular_expression(const char * pattern);

  /// Set and compile new pattern, return false on error.
  bool compile(const char * pattern);

  // Get pattern from last compile().
  const char * get_pattern() const
    { return m_pattern.c_str(); }

  /// Get error message from last compile().
  const char * get_errmsg() const
    { return m_errmsg.c_str(); }

  // Return true if pattern is not set or bad.
  bool empty() const
    { return (m_pattern.empty() || !m_errmsg.empty()); }

  /// Return true if full string matches pattern
  bool full_match(const char * str) const;

#ifdef WITH_CXX11_REGEX
  struct match_range { int rm_so, rm_eo; };
#else
  typedef regmatch_t match_range;
#endif

  /// Return true if substring matches pattern, fill match_range array.
  bool execute(const char * str, unsigned nmatch, match_range * pmatch) const;

private:
  std::string m_pattern;
  std::string m_errmsg;

#ifdef WITH_CXX11_REGEX
  std::regex m_regex;
#else
  regex_t m_regex_buf;
  void free_buf();
  void copy_buf(const regular_expression & x);
#endif

  bool compile();
};

// 128-bit unsigned integer to string conversion.
// Provides full integer precision if compiler supports '__int128'.
// Otherwise precision depends on supported floating point data types.

#if defined(HAVE_LONG_DOUBLE_WIDER) && \
    (!defined(__MINGW32__) || __USE_MINGW_ANSI_STDIO)
    // MinGW 'long double' type does not work with MSVCRT/UCRT *printf()
#define HAVE_LONG_DOUBLE_WIDER_PRINTF 1
#else
#undef HAVE_LONG_DOUBLE_WIDER_PRINTF
#endif

// Return #bits precision provided by uint128_hilo_to_str().
inline int uint128_to_str_precision_bits()
{
#if defined(HAVE___INT128)
  return 128;
#elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF)
  return LDBL_MANT_DIG;
#else
  return DBL_MANT_DIG;
#endif
}

// Convert 128-bit unsigned integer provided as two 64-bit halves to a string.
const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo);

// Version for fixed size buffers.
template <size_t SIZE>
inline const char * uint128_hilo_to_str(char (& str)[SIZE], uint64_t value_hi, uint64_t value_lo)
  { return uint128_hilo_to_str(str, (int)SIZE, value_hi, value_lo); }

/// Get microseconds since some unspecified starting point.
/// Used only for command duration measurements in debug outputs.
/// Returns -1 if unsupported.
long long get_timer_usec();

#ifdef _WIN32
// Get exe directory
//(implemented in os_win32.cpp)
std::string get_exe_dir();
#endif


#ifdef OLD_INTERFACE
// remaining controller types in old interface modules
#define CONTROLLER_UNKNOWN              0x00
#define CONTROLLER_ATA                  0x01
#define CONTROLLER_SCSI                 0x02
#endif

#endif