mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Cleanup: Move *_loc formatting functions into new format.cpp file
qthelper.cpp is already quite voluminous. Move the recently introduced localized versions of (v)snprintf() and put_format() into their own translation unit. Moreover, adopt C-style semantics for asprintf_loc(). This function will be used to remove fixed-size buffers in core/plannernotes.c. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
36249f2780
commit
5afe1a53d8
8 changed files with 448 additions and 405 deletions
|
@ -77,6 +77,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
|
||||||
# dirk ported some core functionality to c++.
|
# dirk ported some core functionality to c++.
|
||||||
qthelper.cpp
|
qthelper.cpp
|
||||||
metadata.cpp
|
metadata.cpp
|
||||||
|
format.cpp
|
||||||
divecomputer.cpp
|
divecomputer.cpp
|
||||||
exif.cpp
|
exif.cpp
|
||||||
subsurfacesysinfo.cpp
|
subsurfacesysinfo.cpp
|
||||||
|
|
414
core/format.cpp
Normal file
414
core/format.cpp
Normal file
|
@ -0,0 +1,414 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include "format.h"
|
||||||
|
#include "membuffer.h"
|
||||||
|
|
||||||
|
QString qasprintf_loc(const char *cformat, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, cformat);
|
||||||
|
QString res = vqasprintf_loc(cformat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vasprintf_flags {
|
||||||
|
bool alternate_form : 1; // TODO: unsupported
|
||||||
|
bool zero : 1;
|
||||||
|
bool left : 1;
|
||||||
|
bool space : 1;
|
||||||
|
bool sign : 1;
|
||||||
|
bool thousands : 1; // ignored
|
||||||
|
};
|
||||||
|
|
||||||
|
enum length_modifier_t {
|
||||||
|
LM_NONE,
|
||||||
|
LM_CHAR,
|
||||||
|
LM_SHORT,
|
||||||
|
LM_LONG,
|
||||||
|
LM_LONGLONG,
|
||||||
|
LM_LONGDOUBLE,
|
||||||
|
LM_INTMAX,
|
||||||
|
LM_SIZET,
|
||||||
|
LM_PTRDIFF
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to insert '+' or ' ' after last space
|
||||||
|
static QString insert_sign(QString s, char sign)
|
||||||
|
{
|
||||||
|
// For space we can take a shortcut: insert in front
|
||||||
|
if (sign == ' ')
|
||||||
|
return sign + s;
|
||||||
|
int size = s.size();
|
||||||
|
int pos;
|
||||||
|
for (pos = 0; pos < size && s[pos].isSpace(); ++pos)
|
||||||
|
; // Pass
|
||||||
|
return s.left(pos) + '+' + s.mid(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString fmt_string(const QString &s, vasprintf_flags flags, int field_width, int precision)
|
||||||
|
{
|
||||||
|
int size = s.size();
|
||||||
|
if (precision >= 0 && size > precision)
|
||||||
|
return s.left(precision);
|
||||||
|
return flags.left ? s.leftJustified(field_width) :
|
||||||
|
s.rightJustified(field_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatting of integers and doubles using Qt's localized functions.
|
||||||
|
// The code is somewhat complex because Qt doesn't support all stdio
|
||||||
|
// format options, notably '+' and ' '.
|
||||||
|
// TODO: Since this is a templated function, remove common code
|
||||||
|
template <typename T>
|
||||||
|
static QString fmt_int(T i, vasprintf_flags flags, int field_width, int precision, int base)
|
||||||
|
{
|
||||||
|
// If precision is given, things are a bit different: we have to pad with zero *and* space.
|
||||||
|
// Therefore, treat this case separately.
|
||||||
|
if (precision > 1) {
|
||||||
|
// For negative numbers, increase precision by one, so that we get
|
||||||
|
// the correct number of printed digits
|
||||||
|
if (i < 0)
|
||||||
|
++precision;
|
||||||
|
QChar fillChar = '0';
|
||||||
|
QString res = QStringLiteral("%L1").arg(i, precision, base, fillChar);
|
||||||
|
if (i >= 0 && flags.space)
|
||||||
|
res = ' ' + res;
|
||||||
|
else if (i >= 0 && flags.sign)
|
||||||
|
res = '+' + res;
|
||||||
|
return fmt_string(res, flags, field_width, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have to prepend a '+' or a space character, remove that from the field width
|
||||||
|
char sign = 0;
|
||||||
|
if (i >= 0 && (flags.space || flags.sign) && field_width > 0) {
|
||||||
|
sign = flags.sign ? '+' : ' ';
|
||||||
|
--field_width;
|
||||||
|
}
|
||||||
|
if (flags.left)
|
||||||
|
field_width = -field_width;
|
||||||
|
QChar fillChar = flags.zero && !flags.left ? '0' : ' ';
|
||||||
|
QString res = QStringLiteral("%L1").arg(i, field_width, base, fillChar);
|
||||||
|
return sign ? insert_sign(res, sign) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString fmt_float(double d, char type, vasprintf_flags flags, int field_width, int precision)
|
||||||
|
{
|
||||||
|
// If we have to prepend a '+' or a space character, remove that from the field width
|
||||||
|
char sign = 0;
|
||||||
|
if (d >= 0.0 && (flags.space || flags.sign) && field_width > 0) {
|
||||||
|
sign = flags.sign ? '+' : ' ';
|
||||||
|
--field_width;
|
||||||
|
}
|
||||||
|
if (flags.left)
|
||||||
|
field_width = -field_width;
|
||||||
|
QChar fillChar = flags.zero && !flags.left ? '0' : ' ';
|
||||||
|
QString res = QStringLiteral("%L1").arg(d, field_width, type, precision, fillChar);
|
||||||
|
return sign ? insert_sign(res, sign) : res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to extract integers from C-style format strings.
|
||||||
|
// The default returned value, if no digits are found is 0.
|
||||||
|
static int parse_fmt_int(const char **act)
|
||||||
|
{
|
||||||
|
if (!isdigit(**act))
|
||||||
|
return 0;
|
||||||
|
int res = 0;
|
||||||
|
while (isdigit(**act)) {
|
||||||
|
res = res * 10 + **act - '0';
|
||||||
|
++(*act);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString vqasprintf_loc(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
const char *act = fmt;
|
||||||
|
QString ret;
|
||||||
|
for (;;) {
|
||||||
|
// Get all bytes up to next '%' character and add them as UTF-8
|
||||||
|
const char *begin = act;
|
||||||
|
while (*act && *act != '%')
|
||||||
|
++act;
|
||||||
|
int len = act - begin;
|
||||||
|
if (len > 0)
|
||||||
|
ret += QString::fromUtf8(begin, len);
|
||||||
|
|
||||||
|
// We found either a '%' or the end of the format string
|
||||||
|
if (!*act)
|
||||||
|
break;
|
||||||
|
++act; // Jump over '%'
|
||||||
|
|
||||||
|
if (*act == '%') {
|
||||||
|
++act;
|
||||||
|
ret += '%';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Flags
|
||||||
|
vasprintf_flags flags = { 0 };
|
||||||
|
for (;; ++act) {
|
||||||
|
switch(*act) {
|
||||||
|
case '#':
|
||||||
|
flags.alternate_form = true;
|
||||||
|
continue;
|
||||||
|
case '0':
|
||||||
|
flags.zero = true;
|
||||||
|
continue;
|
||||||
|
case '-':
|
||||||
|
flags.left = true;
|
||||||
|
continue;
|
||||||
|
case ' ':
|
||||||
|
flags.space = true;
|
||||||
|
continue;
|
||||||
|
case '+':
|
||||||
|
flags.sign = true;
|
||||||
|
continue;
|
||||||
|
case '\'':
|
||||||
|
flags.thousands = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field width
|
||||||
|
int field_width;
|
||||||
|
if (*act == '*') {
|
||||||
|
field_width = va_arg(ap, int);
|
||||||
|
++act;
|
||||||
|
} else {
|
||||||
|
field_width = parse_fmt_int(&act);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precision
|
||||||
|
int precision = -1;
|
||||||
|
if (*act == '.') {
|
||||||
|
++act;
|
||||||
|
if (*act == '*') {
|
||||||
|
precision = va_arg(ap, int);
|
||||||
|
++act;
|
||||||
|
} else {
|
||||||
|
precision = parse_fmt_int(&act);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length modifier
|
||||||
|
enum length_modifier_t length_modifier = LM_NONE;
|
||||||
|
switch(*act) {
|
||||||
|
case 'h':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_CHAR;
|
||||||
|
if (*act == 'h') {
|
||||||
|
length_modifier = LM_SHORT;
|
||||||
|
++act;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_LONG;
|
||||||
|
if (*act == 'l') {
|
||||||
|
length_modifier = LM_LONGLONG;
|
||||||
|
++act;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_LONGLONG;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_LONGDOUBLE;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_INTMAX;
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
case 'Z':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_SIZET;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
++act;
|
||||||
|
length_modifier = LM_PTRDIFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char type = *act++;
|
||||||
|
// Bail out if we reached end of the format string
|
||||||
|
if (!type)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int base = 10;
|
||||||
|
if (type == 'o')
|
||||||
|
base = 8;
|
||||||
|
else if (type == 'x' || type == 'X')
|
||||||
|
base = 16;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case 'd': case 'i': {
|
||||||
|
switch(length_modifier) {
|
||||||
|
case LM_LONG:
|
||||||
|
ret += fmt_int(va_arg(ap, long), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_LONGLONG:
|
||||||
|
ret += fmt_int(va_arg(ap, long long), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_INTMAX:
|
||||||
|
ret += fmt_int(va_arg(ap, intmax_t), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_SIZET:
|
||||||
|
ret += fmt_int(va_arg(ap, ssize_t), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_PTRDIFF:
|
||||||
|
ret += fmt_int(va_arg(ap, ptrdiff_t), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_CHAR:
|
||||||
|
// char is promoted to int when passed through '...'
|
||||||
|
ret += fmt_int(static_cast<char>(va_arg(ap, int)), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_SHORT:
|
||||||
|
// short is promoted to int when passed through '...'
|
||||||
|
ret += fmt_int(static_cast<short>(va_arg(ap, int)), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret += fmt_int(va_arg(ap, int), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'o': case 'u': case 'x': case 'X': {
|
||||||
|
QString s;
|
||||||
|
switch(length_modifier) {
|
||||||
|
case LM_LONG:
|
||||||
|
s = fmt_int(va_arg(ap, unsigned long), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_LONGLONG:
|
||||||
|
s = fmt_int(va_arg(ap, unsigned long long), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_INTMAX:
|
||||||
|
s = fmt_int(va_arg(ap, uintmax_t), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_SIZET:
|
||||||
|
s = fmt_int(va_arg(ap, size_t), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_PTRDIFF:
|
||||||
|
s = fmt_int(va_arg(ap, ptrdiff_t), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_CHAR:
|
||||||
|
// char is promoted to int when passed through '...'
|
||||||
|
s = fmt_int(static_cast<unsigned char>(va_arg(ap, int)), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
case LM_SHORT:
|
||||||
|
// short is promoted to int when passed through '...'
|
||||||
|
s = fmt_int(static_cast<unsigned short>(va_arg(ap, int)), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s = fmt_int(va_arg(ap, unsigned int), flags, field_width, precision, base);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (type == 'X')
|
||||||
|
s = s.toUpper();
|
||||||
|
ret += s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': {
|
||||||
|
// It seems that Qt is not able to format long doubles,
|
||||||
|
// therefore we have to cast down to double.
|
||||||
|
double f = length_modifier == LM_LONGDOUBLE ?
|
||||||
|
static_cast<double>(va_arg(ap, long double)) :
|
||||||
|
va_arg(ap, double);
|
||||||
|
ret += fmt_float(f, type, flags, field_width, precision);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c':
|
||||||
|
if (length_modifier == LM_LONG) {
|
||||||
|
// Cool, on some platforms wint_t is short, on some int.
|
||||||
|
#if WINT_MAX < UINT_MAX
|
||||||
|
wint_t wc = static_cast<wint_t>(va_arg(ap, int));
|
||||||
|
#else
|
||||||
|
wint_t wc = va_arg(ap, wint_t);
|
||||||
|
#endif
|
||||||
|
ret += QChar(wc);
|
||||||
|
} else {
|
||||||
|
ret += static_cast<char>(va_arg(ap, int));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 's': {
|
||||||
|
QString s = length_modifier == LM_LONG ?
|
||||||
|
QString::fromWCharArray(va_arg(ap, wchar_t *)) :
|
||||||
|
QString::fromUtf8(va_arg(ap, char *));
|
||||||
|
ret += fmt_string(s, flags, field_width, precision);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p':
|
||||||
|
ret += QString("0x%1").arg(reinterpret_cast<long long>(va_arg(ap, void *)), field_width, 16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put a formated string respecting the default locale into a C-style array in UTF-8 encoding.
|
||||||
|
// The only complication arises from the fact that we don't want to cut through multi-byte UTF-8 code points.
|
||||||
|
extern "C" int snprintf_loc(char *dst, size_t size, const char *cformat, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, cformat);
|
||||||
|
int res = vsnprintf_loc(dst, size, cformat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int vsnprintf_loc(char *dst, size_t size, const char *cformat, va_list ap)
|
||||||
|
{
|
||||||
|
QByteArray utf8 = vqasprintf_loc(cformat, ap).toUtf8();
|
||||||
|
const char *data = utf8.constData();
|
||||||
|
size_t utf8_size = utf8.size();
|
||||||
|
if (size == 0)
|
||||||
|
return utf8_size;
|
||||||
|
if (size < utf8_size + 1) {
|
||||||
|
memcpy(dst, data, size - 1);
|
||||||
|
if ((data[size - 1] & 0xC0) == 0x80) {
|
||||||
|
// We truncated a multi-byte UTF-8 encoding.
|
||||||
|
--size;
|
||||||
|
// Jump to last copied byte.
|
||||||
|
if (size > 0)
|
||||||
|
--size;
|
||||||
|
while(size > 0 && (dst[size] & 0xC0) == 0x80)
|
||||||
|
--size;
|
||||||
|
dst[size] = 0;
|
||||||
|
} else {
|
||||||
|
dst[size - 1] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(dst, data, utf8_size + 1); // QByteArray guarantees a trailing 0
|
||||||
|
}
|
||||||
|
return utf8_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int asprintf_loc(char **dst, const char *cformat, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, cformat);
|
||||||
|
int res = vasprintf_loc(dst, cformat, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vasprintf_loc(char **dst, const char *cformat, va_list ap)
|
||||||
|
{
|
||||||
|
QByteArray utf8 = vqasprintf_loc(cformat, ap).toUtf8();
|
||||||
|
*dst = strdup(utf8.constData());
|
||||||
|
return utf8.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is defined here instead of membuffer.c, because it needs to access QString.
|
||||||
|
extern "C" void put_vformat_loc(struct membuffer *b, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
QByteArray utf8 = vqasprintf_loc(fmt, args).toUtf8();
|
||||||
|
const char *data = utf8.constData();
|
||||||
|
size_t utf8_size = utf8.size();
|
||||||
|
|
||||||
|
make_room(b, utf8_size);
|
||||||
|
memcpy(b->buffer + b->len, data, utf8_size);
|
||||||
|
b->len += utf8_size;
|
||||||
|
}
|
||||||
|
|
29
core/format.h
Normal file
29
core/format.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef FORMAT_H
|
||||||
|
#define FORMAT_H
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
|
||||||
|
#else
|
||||||
|
#define __printf(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <QString>
|
||||||
|
__printf(1, 2) QString qasprintf_loc(const char *cformat, ...);
|
||||||
|
__printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__printf(3, 4) int snprintf_loc(char *dst, size_t size, const char *cformat, ...);
|
||||||
|
__printf(3, 0) int vsnprintf_loc(char *dst, size_t size, const char *cformat, va_list ap);
|
||||||
|
__printf(2, 3) int asprintf_loc(char **dst, const char *cformat, ...);
|
||||||
|
__printf(2, 0) int vasprintf_loc(char **dst, const char *cformat, va_list ap);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -16,6 +16,7 @@
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "libdivecomputer/parser.h"
|
#include "libdivecomputer/parser.h"
|
||||||
#include "qthelper.h"
|
#include "qthelper.h"
|
||||||
|
#include "format.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
int diveplan_duration(struct diveplan *diveplan)
|
int diveplan_duration(struct diveplan *diveplan)
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "libdivecomputer/version.h"
|
#include "libdivecomputer/version.h"
|
||||||
#include "membuffer.h"
|
#include "membuffer.h"
|
||||||
#include "qthelper.h"
|
#include "qthelper.h"
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
//#define DEBUG_GAS 1
|
//#define DEBUG_GAS 1
|
||||||
|
|
||||||
|
|
|
@ -1722,400 +1722,6 @@ extern "C" void unlock_planner()
|
||||||
planLock.unlock();
|
planLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString asprintf_loc(const char *cformat, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, cformat);
|
|
||||||
QString res = vasprintf_loc(cformat, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct vasprintf_flags {
|
|
||||||
bool alternate_form : 1; // TODO: unsupported
|
|
||||||
bool zero : 1;
|
|
||||||
bool left : 1;
|
|
||||||
bool space : 1;
|
|
||||||
bool sign : 1;
|
|
||||||
bool thousands : 1; // ignored
|
|
||||||
};
|
|
||||||
|
|
||||||
enum length_modifier_t {
|
|
||||||
LM_NONE,
|
|
||||||
LM_CHAR,
|
|
||||||
LM_SHORT,
|
|
||||||
LM_LONG,
|
|
||||||
LM_LONGLONG,
|
|
||||||
LM_LONGDOUBLE,
|
|
||||||
LM_INTMAX,
|
|
||||||
LM_SIZET,
|
|
||||||
LM_PTRDIFF
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to insert '+' or ' ' after last space
|
|
||||||
static QString insert_sign(QString s, char sign)
|
|
||||||
{
|
|
||||||
// For space we can take a shortcut: insert in front
|
|
||||||
if (sign == ' ')
|
|
||||||
return sign + s;
|
|
||||||
int size = s.size();
|
|
||||||
int pos;
|
|
||||||
for (pos = 0; pos < size && s[pos].isSpace(); ++pos)
|
|
||||||
; // Pass
|
|
||||||
return s.left(pos) + '+' + s.mid(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString fmt_string(const QString &s, vasprintf_flags flags, int field_width, int precision)
|
|
||||||
{
|
|
||||||
int size = s.size();
|
|
||||||
if (precision >= 0 && size > precision)
|
|
||||||
return s.left(precision);
|
|
||||||
return flags.left ? s.leftJustified(field_width) :
|
|
||||||
s.rightJustified(field_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatting of integers and doubles using Qt's localized functions.
|
|
||||||
// The code is somewhat complex because Qt doesn't support all stdio
|
|
||||||
// format options, notably '+' and ' '.
|
|
||||||
// TODO: Since this is a templated function, remove common code
|
|
||||||
template <typename T>
|
|
||||||
static QString fmt_int(T i, vasprintf_flags flags, int field_width, int precision, int base)
|
|
||||||
{
|
|
||||||
// If precision is given, things are a bit different: we have to pad with zero *and* space.
|
|
||||||
// Therefore, treat this case separately.
|
|
||||||
if (precision > 1) {
|
|
||||||
// For negative numbers, increase precision by one, so that we get
|
|
||||||
// the correct number of printed digits
|
|
||||||
if (i < 0)
|
|
||||||
++precision;
|
|
||||||
QChar fillChar = '0';
|
|
||||||
QString res = QStringLiteral("%L1").arg(i, precision, base, fillChar);
|
|
||||||
if (i >= 0 && flags.space)
|
|
||||||
res = ' ' + res;
|
|
||||||
else if (i >= 0 && flags.sign)
|
|
||||||
res = '+' + res;
|
|
||||||
return fmt_string(res, flags, field_width, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have to prepend a '+' or a space character, remove that from the field width
|
|
||||||
char sign = 0;
|
|
||||||
if (i >= 0 && (flags.space || flags.sign) && field_width > 0) {
|
|
||||||
sign = flags.sign ? '+' : ' ';
|
|
||||||
--field_width;
|
|
||||||
}
|
|
||||||
if (flags.left)
|
|
||||||
field_width = -field_width;
|
|
||||||
QChar fillChar = flags.zero && !flags.left ? '0' : ' ';
|
|
||||||
QString res = QStringLiteral("%L1").arg(i, field_width, base, fillChar);
|
|
||||||
return sign ? insert_sign(res, sign) : res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString fmt_float(double d, char type, vasprintf_flags flags, int field_width, int precision)
|
|
||||||
{
|
|
||||||
// If we have to prepend a '+' or a space character, remove that from the field width
|
|
||||||
char sign = 0;
|
|
||||||
if (d >= 0.0 && (flags.space || flags.sign) && field_width > 0) {
|
|
||||||
sign = flags.sign ? '+' : ' ';
|
|
||||||
--field_width;
|
|
||||||
}
|
|
||||||
if (flags.left)
|
|
||||||
field_width = -field_width;
|
|
||||||
QChar fillChar = flags.zero && !flags.left ? '0' : ' ';
|
|
||||||
QString res = QStringLiteral("%L1").arg(d, field_width, type, precision, fillChar);
|
|
||||||
return sign ? insert_sign(res, sign) : res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to extract integers from C-style format strings.
|
|
||||||
// The default returned value, if no digits are found is 0.
|
|
||||||
static int parse_fmt_int(const char **act)
|
|
||||||
{
|
|
||||||
if (!isdigit(**act))
|
|
||||||
return 0;
|
|
||||||
int res = 0;
|
|
||||||
while (isdigit(**act)) {
|
|
||||||
res = res * 10 + **act - '0';
|
|
||||||
++(*act);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString vasprintf_loc(const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
const char *act = fmt;
|
|
||||||
QString ret;
|
|
||||||
for (;;) {
|
|
||||||
// Get all bytes up to next '%' character and add them as UTF-8
|
|
||||||
const char *begin = act;
|
|
||||||
while (*act && *act != '%')
|
|
||||||
++act;
|
|
||||||
int len = act - begin;
|
|
||||||
if (len > 0)
|
|
||||||
ret += QString::fromUtf8(begin, len);
|
|
||||||
|
|
||||||
// We found either a '%' or the end of the format string
|
|
||||||
if (!*act)
|
|
||||||
break;
|
|
||||||
++act; // Jump over '%'
|
|
||||||
|
|
||||||
if (*act == '%') {
|
|
||||||
++act;
|
|
||||||
ret += '%';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Flags
|
|
||||||
vasprintf_flags flags = { 0 };
|
|
||||||
for (;; ++act) {
|
|
||||||
switch(*act) {
|
|
||||||
case '#':
|
|
||||||
flags.alternate_form = true;
|
|
||||||
continue;
|
|
||||||
case '0':
|
|
||||||
flags.zero = true;
|
|
||||||
continue;
|
|
||||||
case '-':
|
|
||||||
flags.left = true;
|
|
||||||
continue;
|
|
||||||
case ' ':
|
|
||||||
flags.space = true;
|
|
||||||
continue;
|
|
||||||
case '+':
|
|
||||||
flags.sign = true;
|
|
||||||
continue;
|
|
||||||
case '\'':
|
|
||||||
flags.thousands = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field width
|
|
||||||
int field_width;
|
|
||||||
if (*act == '*') {
|
|
||||||
field_width = va_arg(ap, int);
|
|
||||||
++act;
|
|
||||||
} else {
|
|
||||||
field_width = parse_fmt_int(&act);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precision
|
|
||||||
int precision = -1;
|
|
||||||
if (*act == '.') {
|
|
||||||
++act;
|
|
||||||
if (*act == '*') {
|
|
||||||
precision = va_arg(ap, int);
|
|
||||||
++act;
|
|
||||||
} else {
|
|
||||||
precision = parse_fmt_int(&act);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length modifier
|
|
||||||
enum length_modifier_t length_modifier = LM_NONE;
|
|
||||||
switch(*act) {
|
|
||||||
case 'h':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_CHAR;
|
|
||||||
if (*act == 'h') {
|
|
||||||
length_modifier = LM_SHORT;
|
|
||||||
++act;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_LONG;
|
|
||||||
if (*act == 'l') {
|
|
||||||
length_modifier = LM_LONGLONG;
|
|
||||||
++act;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_LONGLONG;
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_LONGDOUBLE;
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_INTMAX;
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
case 'Z':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_SIZET;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
++act;
|
|
||||||
length_modifier = LM_PTRDIFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char type = *act++;
|
|
||||||
// Bail out if we reached end of the format string
|
|
||||||
if (!type)
|
|
||||||
break;
|
|
||||||
|
|
||||||
int base = 10;
|
|
||||||
if (type == 'o')
|
|
||||||
base = 8;
|
|
||||||
else if (type == 'x' || type == 'X')
|
|
||||||
base = 16;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case 'd': case 'i': {
|
|
||||||
switch(length_modifier) {
|
|
||||||
case LM_LONG:
|
|
||||||
ret += fmt_int(va_arg(ap, long), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_LONGLONG:
|
|
||||||
ret += fmt_int(va_arg(ap, long long), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_INTMAX:
|
|
||||||
ret += fmt_int(va_arg(ap, intmax_t), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_SIZET:
|
|
||||||
ret += fmt_int(va_arg(ap, ssize_t), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_PTRDIFF:
|
|
||||||
ret += fmt_int(va_arg(ap, ptrdiff_t), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_CHAR:
|
|
||||||
// char is promoted to int when passed through '...'
|
|
||||||
ret += fmt_int(static_cast<char>(va_arg(ap, int)), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_SHORT:
|
|
||||||
// short is promoted to int when passed through '...'
|
|
||||||
ret += fmt_int(static_cast<short>(va_arg(ap, int)), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret += fmt_int(va_arg(ap, int), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'o': case 'u': case 'x': case 'X': {
|
|
||||||
QString s;
|
|
||||||
switch(length_modifier) {
|
|
||||||
case LM_LONG:
|
|
||||||
s = fmt_int(va_arg(ap, unsigned long), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_LONGLONG:
|
|
||||||
s = fmt_int(va_arg(ap, unsigned long long), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_INTMAX:
|
|
||||||
s = fmt_int(va_arg(ap, uintmax_t), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_SIZET:
|
|
||||||
s = fmt_int(va_arg(ap, size_t), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_PTRDIFF:
|
|
||||||
s = fmt_int(va_arg(ap, ptrdiff_t), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_CHAR:
|
|
||||||
// char is promoted to int when passed through '...'
|
|
||||||
s = fmt_int(static_cast<unsigned char>(va_arg(ap, int)), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
case LM_SHORT:
|
|
||||||
// short is promoted to int when passed through '...'
|
|
||||||
s = fmt_int(static_cast<unsigned short>(va_arg(ap, int)), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
s = fmt_int(va_arg(ap, unsigned int), flags, field_width, precision, base);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (type == 'X')
|
|
||||||
s = s.toUpper();
|
|
||||||
ret += s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': {
|
|
||||||
// It seems that Qt is not able to format long doubles,
|
|
||||||
// therefore we have to cast down to double.
|
|
||||||
double f = length_modifier == LM_LONGDOUBLE ?
|
|
||||||
static_cast<double>(va_arg(ap, long double)) :
|
|
||||||
va_arg(ap, double);
|
|
||||||
ret += fmt_float(f, type, flags, field_width, precision);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'c':
|
|
||||||
if (length_modifier == LM_LONG) {
|
|
||||||
// Cool, on some platforms wint_t is short, on some int.
|
|
||||||
#if WINT_MAX < UINT_MAX
|
|
||||||
wint_t wc = static_cast<wint_t>(va_arg(ap, int));
|
|
||||||
#else
|
|
||||||
wint_t wc = va_arg(ap, wint_t);
|
|
||||||
#endif
|
|
||||||
ret += QChar(wc);
|
|
||||||
} else {
|
|
||||||
ret += static_cast<char>(va_arg(ap, int));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's': {
|
|
||||||
QString s = length_modifier == LM_LONG ?
|
|
||||||
QString::fromWCharArray(va_arg(ap, wchar_t *)) :
|
|
||||||
QString::fromUtf8(va_arg(ap, char *));
|
|
||||||
ret += fmt_string(s, flags, field_width, precision);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'p':
|
|
||||||
ret += QString("0x%1").arg(reinterpret_cast<long long>(va_arg(ap, void *)), field_width, 16);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put a formated string respecting the default locale into a C-style array in UTF-8 encoding.
|
|
||||||
// The only complication arises from the fact that we don't want to cut through multi-byte UTF-8 code points.
|
|
||||||
extern "C" int snprintf_loc(char *dst, size_t size, const char *cformat, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, cformat);
|
|
||||||
int res = vsnprintf_loc(dst, size, cformat, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int vsnprintf_loc(char *dst, size_t size, const char *cformat, va_list ap)
|
|
||||||
{
|
|
||||||
QByteArray utf8 = vasprintf_loc(cformat, ap).toUtf8();
|
|
||||||
const char *data = utf8.constData();
|
|
||||||
size_t utf8_size = utf8.size();
|
|
||||||
if (size == 0)
|
|
||||||
return utf8_size;
|
|
||||||
if (size < utf8_size + 1) {
|
|
||||||
memcpy(dst, data, size - 1);
|
|
||||||
if ((data[size - 1] & 0xC0) == 0x80) {
|
|
||||||
// We truncated a multi-byte UTF-8 encoding.
|
|
||||||
--size;
|
|
||||||
// Jump to last copied byte.
|
|
||||||
if (size > 0)
|
|
||||||
--size;
|
|
||||||
while(size > 0 && (dst[size] & 0xC0) == 0x80)
|
|
||||||
--size;
|
|
||||||
dst[size] = 0;
|
|
||||||
} else {
|
|
||||||
dst[size - 1] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
memcpy(dst, data, utf8_size + 1); // QByteArray guarantees a trailing 0
|
|
||||||
}
|
|
||||||
return utf8_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function is defined here instead of membuffer.c, because it needs to access QString.
|
|
||||||
extern "C" void put_vformat_loc(struct membuffer *b, const char *fmt, va_list args)
|
|
||||||
{
|
|
||||||
QByteArray utf8 = vasprintf_loc(fmt, args).toUtf8();
|
|
||||||
const char *data = utf8.constData();
|
|
||||||
size_t utf8_size = utf8.size();
|
|
||||||
|
|
||||||
make_room(b, utf8_size);
|
|
||||||
memcpy(b->buffer + b->len, data, utf8_size);
|
|
||||||
b->len += utf8_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *copy_qstring(const QString &s)
|
char *copy_qstring(const QString &s)
|
||||||
{
|
{
|
||||||
return strdup(qPrintable(s));
|
return strdup(qPrintable(s));
|
||||||
|
|
|
@ -6,13 +6,7 @@
|
||||||
#include "dive.h"
|
#include "dive.h"
|
||||||
#include "divelist.h"
|
#include "divelist.h"
|
||||||
|
|
||||||
// 1) Types and macros
|
// 1) Types
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
|
|
||||||
#else
|
|
||||||
#define __printf(x, y)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum inertgas {N2, HE};
|
enum inertgas {N2, HE};
|
||||||
|
|
||||||
|
@ -56,8 +50,6 @@ QString getUUID();
|
||||||
QStringList imageExtensionFilters();
|
QStringList imageExtensionFilters();
|
||||||
char *intdup(int index);
|
char *intdup(int index);
|
||||||
char *copy_qstring(const QString &);
|
char *copy_qstring(const QString &);
|
||||||
__printf(1, 2) QString asprintf_loc(const char *cformat, ...);
|
|
||||||
__printf(1, 0) QString vasprintf_loc(const char *cformat, va_list ap);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 3) Functions visible to C and C++
|
// 3) Functions visible to C and C++
|
||||||
|
@ -93,8 +85,6 @@ void cache_insert(int tissue, int timestep, enum inertgas gas, double value);
|
||||||
void print_qt_versions();
|
void print_qt_versions();
|
||||||
void lock_planner();
|
void lock_planner();
|
||||||
void unlock_planner();
|
void unlock_planner();
|
||||||
__printf(3, 4) int snprintf_loc(char *dst, size_t size, const char *cformat, ...);
|
|
||||||
__printf(3, 0) int vsnprintf_loc(char *dst, size_t size, const char *cformat, va_list ap);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ SOURCES += ../../../subsurface-mobile-main.cpp \
|
||||||
../../../core/divesitehelpers.cpp \
|
../../../core/divesitehelpers.cpp \
|
||||||
../../../core/errorhelper.c \
|
../../../core/errorhelper.c \
|
||||||
../../../core/exif.cpp \
|
../../../core/exif.cpp \
|
||||||
|
../../../core/format.cpp \
|
||||||
../../../core/gettextfromc.cpp \
|
../../../core/gettextfromc.cpp \
|
||||||
../../../core/isocialnetworkintegration.cpp \
|
../../../core/isocialnetworkintegration.cpp \
|
||||||
../../../core/metrics.cpp \
|
../../../core/metrics.cpp \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue