Coverage Report

Created: 2025-07-18 06:36

/src/exiv2/src/value.cpp
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
// included header files
4
#include "value.hpp"
5
6
#include "convert.hpp"
7
#include "enforce.hpp"
8
#include "error.hpp"
9
#include "types.hpp"
10
11
#include "image_int.hpp"
12
13
// + standard includes
14
#include <sstream>
15
16
// *****************************************************************************
17
// class member definitions
18
namespace Exiv2 {
19
79.4M
Value::Value(TypeId typeId) : type_(typeId) {
20
79.4M
}
21
22
30.1M
Value::UniquePtr Value::create(TypeId typeId) {
23
30.1M
  switch (typeId) {
24
0
    case invalidTypeId:
25
6.47k
    case signedByte:
26
2.83M
    case unsignedByte:
27
2.83M
      return std::make_unique<DataValue>(typeId);
28
88.8k
    case asciiString:
29
88.8k
      return std::make_unique<AsciiValue>();
30
21.7M
    case unsignedShort:
31
21.7M
      return std::make_unique<ValueType<uint16_t>>();
32
221k
    case unsignedLong:
33
240k
    case tiffIfd:
34
240k
      return std::make_unique<ValueType<uint32_t>>(typeId);
35
41.7k
    case unsignedRational:
36
41.7k
      return std::make_unique<ValueType<URational>>();
37
99.2k
    case undefined:
38
99.2k
      return std::make_unique<DataValue>();
39
2.41M
    case signedShort:
40
2.41M
      return std::make_unique<ValueType<int16_t>>();
41
88.4k
    case signedLong:
42
88.4k
      return std::make_unique<ValueType<int32_t>>();
43
27.0k
    case signedRational:
44
27.0k
      return std::make_unique<ValueType<Rational>>();
45
7.42k
    case tiffFloat:
46
7.42k
      return std::make_unique<ValueType<float>>();
47
7.42k
    case tiffDouble:
48
7.42k
      return std::make_unique<ValueType<double>>();
49
45.1k
    case string:
50
45.1k
      return std::make_unique<StringValue>();
51
768
    case date:
52
768
      return std::make_unique<DateValue>();
53
1.52k
    case time:
54
1.52k
      return std::make_unique<TimeValue>();
55
1.68k
    case comment:
56
1.68k
      return std::make_unique<CommentValue>();
57
16.4k
    case xmpText:
58
16.4k
      return std::make_unique<XmpTextValue>();
59
0
    case xmpBag:
60
3.25k
    case xmpSeq:
61
3.25k
    case xmpAlt:
62
3.25k
      return std::make_unique<XmpArrayValue>(typeId);
63
0
    case langAlt:
64
0
      return std::make_unique<LangAltValue>();
65
2.39M
    default:
66
2.39M
      return std::make_unique<DataValue>(typeId);
67
30.1M
  }
68
30.1M
}  // Value::create
69
70
1.24k
int Value::setDataArea(const byte* /*buf*/, size_t /*len*/) {
71
1.24k
  return -1;
72
1.24k
}
73
74
92.2k
std::string Value::toString() const {
75
92.2k
  std::ostringstream os;
76
92.2k
  write(os);
77
92.2k
  ok_ = !os.fail();
78
92.2k
  return os.str();
79
92.2k
}
80
81
9.52k
std::string Value::toString(size_t /*n*/) const {
82
9.52k
  return toString();
83
9.52k
}
84
85
12.9k
size_t Value::sizeDataArea() const {
86
12.9k
  return 0;
87
12.9k
}
88
89
280
DataBuf Value::dataArea() const {
90
280
  return {nullptr, 0};
91
280
}
92
93
5.32M
DataValue::DataValue(TypeId typeId) : Value(typeId) {
94
5.32M
}
95
96
0
DataValue::DataValue(const byte* buf, size_t len, ByteOrder byteOrder, TypeId typeId) : Value(typeId) {
97
0
  read(buf, len, byteOrder);
98
0
}
99
100
7.05M
size_t DataValue::count() const {
101
7.05M
  return size();
102
7.05M
}
103
104
5.32M
int DataValue::read(const byte* buf, size_t len, ByteOrder /*byteOrder*/) {
105
  // byteOrder not needed
106
5.32M
  value_.assign(buf, buf + len);
107
5.32M
  return 0;
108
5.32M
}
109
110
0
int DataValue::read(const std::string& buf) {
111
0
  std::istringstream is(buf);
112
0
  int tmp = 0;
113
0
  ValueType val;
114
0
  while (is >> tmp)
115
0
    val.push_back(tmp);
116
0
  if (!is.eof())
117
0
    return 1;
118
0
  value_ = std::move(val);
119
0
  return 0;
120
0
}
121
122
733k
size_t DataValue::copy(byte* buf, ByteOrder /*byteOrder*/) const {
123
  // byteOrder not needed
124
733k
  return std::copy(value_.begin(), value_.end(), buf) - buf;
125
733k
}
126
127
9.67M
size_t DataValue::size() const {
128
9.67M
  return value_.size();
129
9.67M
}
130
131
12.2M
DataValue* DataValue::clone_() const {
132
12.2M
  return new DataValue(*this);
133
12.2M
}
134
135
91.2k
std::ostream& DataValue::write(std::ostream& os) const {
136
91.2k
  if (!value_.empty()) {
137
91.0k
    std::copy(value_.begin(), value_.end() - 1, std::ostream_iterator<int>(os, " "));
138
91.0k
    os << static_cast<int>(value_.back());
139
91.0k
  }
140
91.2k
  return os;
141
91.2k
}
142
143
2.31k
std::string DataValue::toString(size_t n) const {
144
2.31k
  ok_ = true;
145
2.31k
  return std::to_string(value_.at(n));
146
2.31k
}
147
148
129k
int64_t DataValue::toInt64(size_t n) const {
149
129k
  ok_ = true;
150
129k
  return value_.at(n);
151
129k
}
152
153
46.6k
uint32_t DataValue::toUint32(size_t n) const {
154
46.6k
  ok_ = true;
155
46.6k
  return value_.at(n);
156
46.6k
}
157
158
1.34k
float DataValue::toFloat(size_t n) const {
159
1.34k
  ok_ = true;
160
1.34k
  return value_.at(n);
161
1.34k
}
162
163
1.85k
Rational DataValue::toRational(size_t n) const {
164
1.85k
  ok_ = true;
165
1.85k
  return {value_.at(n), 1};
166
1.85k
}
167
168
0
StringValueBase::StringValueBase(TypeId typeId, const std::string& buf) : Value(typeId) {
169
0
  read(buf);
170
0
}
171
172
97
int StringValueBase::read(const std::string& buf) {
173
97
  value_ = buf;
174
97
  return 0;
175
97
}
176
177
124k
int StringValueBase::read(const byte* buf, size_t len, ByteOrder /*byteOrder*/) {
178
  // byteOrder not needed
179
124k
  if (buf)
180
124k
    value_ = std::string(reinterpret_cast<const char*>(buf), len);
181
124k
  return 0;
182
124k
}
183
184
48.3k
size_t StringValueBase::copy(byte* buf, ByteOrder /*byteOrder*/) const {
185
48.3k
  if (value_.empty())
186
17.6k
    return 0;
187
  // byteOrder not needed
188
30.6k
  return value_.copy(reinterpret_cast<char*>(buf), value_.size());
189
48.3k
}
190
191
169k
size_t StringValueBase::count() const {
192
169k
  return size();
193
169k
}
194
195
282k
size_t StringValueBase::size() const {
196
282k
  return value_.size();
197
282k
}
198
199
1.10k
std::ostream& StringValueBase::write(std::ostream& os) const {
200
1.10k
  return os << value_;
201
1.10k
}
202
203
18.1k
int64_t StringValueBase::toInt64(size_t n) const {
204
18.1k
  ok_ = true;
205
18.1k
  return value_.at(n);
206
18.1k
}
207
208
4.60k
uint32_t StringValueBase::toUint32(size_t n) const {
209
4.60k
  ok_ = true;
210
4.60k
  return value_.at(n);
211
4.60k
}
212
213
123
float StringValueBase::toFloat(size_t n) const {
214
123
  ok_ = true;
215
123
  return value_.at(n);
216
123
}
217
218
202
Rational StringValueBase::toRational(size_t n) const {
219
202
  ok_ = true;
220
202
  return {value_.at(n), 1};
221
202
}
222
223
45.1k
StringValue::StringValue() : StringValueBase(string) {
224
45.1k
}
225
226
0
StringValue::StringValue(const std::string& buf) : StringValueBase(string, buf) {
227
0
}
228
229
588k
StringValue* StringValue::clone_() const {
230
588k
  return new StringValue(*this);
231
588k
}
232
233
88.8k
AsciiValue::AsciiValue() : StringValueBase(asciiString) {
234
88.8k
}
235
236
0
AsciiValue::AsciiValue(const std::string& buf) : StringValueBase(asciiString, buf) {
237
0
}
238
239
25.0k
int AsciiValue::read(const std::string& buf) {
240
25.0k
  value_ = buf;
241
  // ensure count>0 and nul terminated # https://github.com/Exiv2/exiv2/issues/1484
242
25.0k
  if (value_.empty() || value_.back() != '\0') {
243
25.0k
    value_ += '\0';
244
25.0k
  }
245
25.0k
  return 0;
246
25.0k
}
247
248
170k
AsciiValue* AsciiValue::clone_() const {
249
170k
  return new AsciiValue(*this);
250
170k
}
251
252
114k
std::ostream& AsciiValue::write(std::ostream& os) const {
253
  // Write only up to the first '\0' (if any)
254
114k
  std::string::size_type pos = value_.find_first_of('\0');
255
114k
  if (pos == std::string::npos)
256
4.28k
    pos = value_.size();
257
114k
  return os << value_.substr(0, pos);
258
114k
}
259
260
//! Lookup list of supported IFD type information
261
constexpr CommentValue::CharsetTable CommentValue::CharsetInfo::charsetTable_[] = {
262
    {ascii, "Ascii", "ASCII\0\0\0"},
263
    {jis, "Jis", "JIS\0\0\0\0\0"},
264
    {unicode, "Unicode", "UNICODE\0"},
265
    {undefined, "Undefined", "\0\0\0\0\0\0\0\0"},
266
    {invalidCharsetId, "InvalidCharsetId", "\0\0\0\0\0\0\0\0"},
267
    {lastCharsetId, "InvalidCharsetId", "\0\0\0\0\0\0\0\0"},
268
};
269
270
684
const char* CommentValue::CharsetInfo::name(CharsetId charsetId) {
271
684
  return charsetTable_[charsetId < lastCharsetId ? charsetId : undefined].name_;
272
684
}
273
274
0
const char* CommentValue::CharsetInfo::code(CharsetId charsetId) {
275
0
  return charsetTable_[charsetId < lastCharsetId ? charsetId : undefined].code_;
276
0
}
277
278
0
CommentValue::CharsetId CommentValue::CharsetInfo::charsetIdByName(const std::string& name) {
279
0
  int i = 0;
280
0
  for (; charsetTable_[i].charsetId_ != lastCharsetId && charsetTable_[i].name_ != name; ++i) {
281
0
  }
282
0
  return charsetTable_[i].charsetId_ == lastCharsetId ? invalidCharsetId : charsetTable_[i].charsetId_;
283
0
}
284
285
3.19k
CommentValue::CharsetId CommentValue::CharsetInfo::charsetIdByCode(const std::string& code) {
286
3.19k
  int i = 0;
287
18.4k
  for (; charsetTable_[i].charsetId_ != lastCharsetId && std::string(charsetTable_[i].code_, 8) != code; ++i) {
288
15.2k
  }
289
3.19k
  return charsetTable_[i].charsetId_ == lastCharsetId ? invalidCharsetId : charsetTable_[i].charsetId_;
290
3.19k
}
291
292
1.68k
CommentValue::CommentValue() : StringValueBase(Exiv2::undefined) {
293
1.68k
}
294
295
0
CommentValue::CommentValue(const std::string& comment) : StringValueBase(Exiv2::undefined) {
296
0
  read(comment);
297
0
}
298
299
0
int CommentValue::read(const std::string& comment) {
300
0
  std::string c = comment;
301
0
  CharsetId charsetId = undefined;
302
0
  if (comment.starts_with("charset=")) {
303
0
    const std::string::size_type pos = comment.find_first_of(' ');
304
0
    std::string name = comment.substr(8, pos - 8);
305
    // Strip quotes (so you can also specify the charset without quotes)
306
0
    if (!name.empty() && name.front() == '"')
307
0
      name = name.substr(1);
308
0
    if (!name.empty() && name.back() == '"')
309
0
      name.pop_back();
310
0
    charsetId = CharsetInfo::charsetIdByName(name);
311
0
    if (charsetId == invalidCharsetId) {
312
0
#ifndef SUPPRESS_WARNINGS
313
0
      EXV_WARNING << Error(ErrorCode::kerInvalidCharset, name) << "\n";
314
0
#endif
315
0
      return 1;
316
0
    }
317
0
    c.clear();
318
0
    if (pos != std::string::npos)
319
0
      c = comment.substr(pos + 1);
320
0
  }
321
0
  if (charsetId == unicode) {
322
0
    const char* to = byteOrder_ == littleEndian ? "UCS-2LE" : "UCS-2BE";
323
0
    convertStringCharset(c, "UTF-8", to);
324
0
  }
325
0
  const std::string code(CharsetInfo::code(charsetId), 8);
326
0
  return StringValueBase::read(code + c);
327
0
}
328
329
1.68k
int CommentValue::read(const byte* buf, size_t len, ByteOrder byteOrder) {
330
1.68k
  byteOrder_ = byteOrder;
331
1.68k
  return StringValueBase::read(buf, len, byteOrder);
332
1.68k
}
333
334
164
size_t CommentValue::copy(byte* buf, ByteOrder byteOrder) const {
335
164
  std::string c = value_;
336
164
  if (charsetId() == unicode) {
337
0
    c = value_.substr(8);
338
0
    [[maybe_unused]] const size_t sz = c.size();
339
0
    if (byteOrder_ == littleEndian && byteOrder == bigEndian) {
340
0
      convertStringCharset(c, "UCS-2LE", "UCS-2BE");
341
0
    } else if (byteOrder_ == bigEndian && byteOrder == littleEndian) {
342
0
      convertStringCharset(c, "UCS-2BE", "UCS-2LE");
343
0
    }
344
0
    c = value_.substr(0, 8) + c;
345
0
  }
346
164
  if (c.empty())
347
45
    return 0;
348
119
  return c.copy(reinterpret_cast<char*>(buf), c.size());
349
164
}
350
351
892
std::ostream& CommentValue::write(std::ostream& os) const {
352
892
  CharsetId csId = charsetId();
353
892
  std::string text = comment();
354
892
  if (csId != undefined) {
355
684
    os << "charset=" << CharsetInfo::name(csId) << " ";
356
684
  }
357
892
  return os << text;
358
892
}
359
360
892
std::string CommentValue::comment(const char* encoding) const {
361
892
  std::string c;
362
892
  if (value_.length() < 8) {
363
90
    return c;
364
90
  }
365
802
  c = value_.substr(8);
366
802
  if (charsetId() == unicode) {
367
0
    const char* from = !encoding || *encoding == '\0' ? detectCharset(c) : encoding;
368
0
    if (!convertStringCharset(c, from, "UTF-8"))
369
0
      throw Error(ErrorCode::kerInvalidIconvEncoding, from, "UTF-8");
370
0
  }
371
372
  // # 1266 Remove trailing nulls
373
802
  if (charsetId() == undefined || charsetId() == ascii) {
374
118
    auto n = c.find('\0');
375
118
    if (n != std::string::npos)
376
94
      c.resize(n);
377
118
  }
378
802
  return c;
379
802
}
380
381
3.34k
CommentValue::CharsetId CommentValue::charsetId() const {
382
3.34k
  CharsetId charsetId = undefined;
383
3.34k
  if (value_.length() >= 8) {
384
3.19k
    const std::string code = value_.substr(0, 8);
385
3.19k
    charsetId = CharsetInfo::charsetIdByCode(code);
386
3.19k
  }
387
3.34k
  return charsetId;
388
3.34k
}
389
390
0
const char* CommentValue::detectCharset(std::string& c) const {
391
  // Interpret a BOM if there is one
392
0
  if (c.front() == '\xef' && c[1] == '\xbb' && c[2] == '\xbf') {
393
0
    c = c.substr(3);
394
0
    return "UTF-8";
395
0
  }
396
0
  if (c.front() == '\xff' && c[1] == '\xfe') {
397
0
    c = c.substr(2);
398
0
    return "UCS-2LE";
399
0
  }
400
0
  if (c.front() == '\xfe' && c[1] == '\xff') {
401
0
    c = c.substr(2);
402
0
    return "UCS-2BE";
403
0
  }
404
405
  // Todo: Add logic to guess if the comment is encoded in UTF-8
406
407
0
  return byteOrder_ == littleEndian ? "UCS-2LE" : "UCS-2BE";
408
0
}
409
410
3.48k
CommentValue* CommentValue::clone_() const {
411
3.48k
  return new CommentValue(*this);
412
3.48k
}
413
414
4.11k
void XmpValue::setXmpArrayType(XmpArrayType xmpArrayType) {
415
4.11k
  xmpArrayType_ = xmpArrayType;
416
4.11k
}
417
418
354
void XmpValue::setXmpStruct(XmpStruct xmpStruct) {
419
354
  xmpStruct_ = xmpStruct;
420
354
}
421
422
24.8k
XmpValue::XmpArrayType XmpValue::xmpArrayType() const {
423
24.8k
  return xmpArrayType_;
424
24.8k
}
425
426
4.05k
XmpValue::XmpArrayType XmpValue::xmpArrayType(TypeId typeId) {
427
4.05k
  XmpArrayType xa = xaNone;
428
4.05k
  switch (typeId) {
429
14
    case xmpAlt:
430
14
      xa = xaAlt;
431
14
      break;
432
72
    case xmpBag:
433
72
      xa = xaBag;
434
72
      break;
435
3.73k
    case xmpSeq:
436
3.73k
      xa = xaSeq;
437
3.73k
      break;
438
242
    default:
439
242
      break;
440
4.05k
  }
441
4.05k
  return xa;
442
4.05k
}
443
444
24.9k
XmpValue::XmpStruct XmpValue::xmpStruct() const {
445
24.9k
  return xmpStruct_;
446
24.9k
}
447
448
0
size_t XmpValue::copy(byte* buf, ByteOrder /*byteOrder*/) const {
449
0
  std::ostringstream os;
450
0
  write(os);
451
0
  std::string s = os.str();
452
0
  if (!s.empty())
453
0
    std::copy(s.begin(), s.end(), buf);
454
0
  return s.size();
455
0
}
456
457
0
int XmpValue::read(const byte* buf, size_t len, ByteOrder /*byteOrder*/) {
458
0
  std::string s(reinterpret_cast<const char*>(buf), len);
459
0
  return read(s);
460
0
}
461
462
0
size_t XmpValue::size() const {
463
0
  std::ostringstream os;
464
0
  write(os);
465
0
  return os.str().size();
466
0
}
467
468
24.3k
XmpTextValue::XmpTextValue() : XmpValue(xmpText) {
469
24.3k
}
470
471
0
XmpTextValue::XmpTextValue(const std::string& buf) : XmpValue(xmpText) {
472
0
  read(buf);
473
0
}
474
475
798k
int XmpTextValue::read(const std::string& buf) {
476
  // support a type=Alt,Bag,Seq,Struct indicator
477
798k
  std::string b = buf;
478
798k
  std::string type;
479
798k
  if (buf.starts_with("type=")) {
480
265
    std::string::size_type pos = buf.find_first_of(' ');
481
265
    type = buf.substr(5, pos - 5);
482
    // Strip quotes (so you can also specify the type without quotes)
483
265
    if (!type.empty() && type.front() == '"')
484
105
      type = type.substr(1);
485
265
    if (!type.empty() && type.back() == '"')
486
66
      type.pop_back();
487
265
    b.clear();
488
265
    if (pos != std::string::npos)
489
144
      b = buf.substr(pos + 1);
490
265
  }
491
798k
  if (!type.empty()) {
492
131
    if (type == "Alt") {
493
12
      setXmpArrayType(XmpValue::xaAlt);
494
119
    } else if (type == "Bag") {
495
29
      setXmpArrayType(XmpValue::xaBag);
496
90
    } else if (type == "Seq") {
497
20
      setXmpArrayType(XmpValue::xaSeq);
498
70
    } else if (type == "Struct") {
499
29
      setXmpStruct();
500
41
    } else {
501
41
      throw Error(ErrorCode::kerInvalidXmpText, type);
502
41
    }
503
131
  }
504
798k
  value_ = std::move(b);
505
798k
  return 0;
506
798k
}
507
508
0
XmpTextValue::UniquePtr XmpTextValue::clone() const {
509
0
  return UniquePtr(clone_());
510
0
}
511
512
19.0k
size_t XmpTextValue::size() const {
513
19.0k
  return value_.size();
514
19.0k
}
515
516
19.0k
size_t XmpTextValue::count() const {
517
19.0k
  return size();
518
19.0k
}
519
520
21.7k
std::ostream& XmpTextValue::write(std::ostream& os) const {
521
21.7k
  bool del = false;
522
21.7k
  if (xmpArrayType() != XmpValue::xaNone) {
523
116
    switch (xmpArrayType()) {
524
6
      case XmpValue::xaAlt:
525
6
        os << "type=\"Alt\"";
526
6
        break;
527
18
      case XmpValue::xaBag:
528
18
        os << "type=\"Bag\"";
529
18
        break;
530
92
      case XmpValue::xaSeq:
531
92
        os << "type=\"Seq\"";
532
92
        break;
533
0
      case XmpValue::xaNone:
534
0
        break;  // just to suppress the warning
535
116
    }
536
116
    del = true;
537
21.6k
  } else if (xmpStruct() != XmpValue::xsNone) {
538
330
    switch (xmpStruct()) {
539
330
      case XmpValue::xsStruct:
540
330
        os << "type=\"Struct\"";
541
330
        break;
542
0
      case XmpValue::xsNone:
543
0
        break;  // just to suppress the warning
544
330
    }
545
330
    del = true;
546
330
  }
547
21.7k
  if (del && !value_.empty())
548
18
    os << " ";
549
21.7k
  return os << value_;
550
21.7k
}
551
552
0
int64_t XmpTextValue::toInt64(size_t /*n*/) const {
553
0
  return parseInt64(value_, ok_);
554
0
}
555
556
0
uint32_t XmpTextValue::toUint32(size_t /*n*/) const {
557
0
  return parseUint32(value_, ok_);
558
0
}
559
560
0
float XmpTextValue::toFloat(size_t /*n*/) const {
561
0
  return parseFloat(value_, ok_);
562
0
}
563
564
0
Rational XmpTextValue::toRational(size_t /*n*/) const {
565
0
  return parseRational(value_, ok_);
566
0
}
567
568
42.5k
XmpTextValue* XmpTextValue::clone_() const {
569
42.5k
  return new XmpTextValue(*this);
570
42.5k
}
571
572
3.73k
XmpArrayValue::XmpArrayValue(TypeId typeId) : XmpValue(typeId) {
573
3.73k
  setXmpArrayType(xmpArrayType(typeId));
574
3.73k
}
575
576
38.6k
int XmpArrayValue::read(const std::string& buf) {
577
38.6k
  if (!buf.empty())
578
27.1k
    value_.push_back(buf);
579
38.6k
  return 0;
580
38.6k
}
581
582
0
XmpArrayValue::UniquePtr XmpArrayValue::clone() const {
583
0
  return UniquePtr(clone_());
584
0
}
585
586
2.56k
size_t XmpArrayValue::count() const {
587
2.56k
  return value_.size();
588
2.56k
}
589
590
1.68k
std::ostream& XmpArrayValue::write(std::ostream& os) const {
591
1.68k
  if (!value_.empty()) {
592
1.18k
    std::copy(value_.begin(), value_.end() - 1, std::ostream_iterator<std::string>(os, ", "));
593
1.18k
    os << value_.back();
594
1.18k
  }
595
1.68k
  return os;
596
1.68k
}
597
598
1.26k
std::string XmpArrayValue::toString(size_t n) const {
599
1.26k
  ok_ = true;
600
1.26k
  return value_.at(n);
601
1.26k
}
602
603
0
int64_t XmpArrayValue::toInt64(size_t n) const {
604
0
  return parseInt64(value_.at(n), ok_);
605
0
}
606
607
0
uint32_t XmpArrayValue::toUint32(size_t n) const {
608
0
  return parseUint32(value_.at(n), ok_);
609
0
}
610
611
0
float XmpArrayValue::toFloat(size_t n) const {
612
0
  return parseFloat(value_.at(n), ok_);
613
0
}
614
615
0
Rational XmpArrayValue::toRational(size_t n) const {
616
0
  return parseRational(value_.at(n), ok_);
617
0
}
618
619
10.8k
XmpArrayValue* XmpArrayValue::clone_() const {
620
10.8k
  return new XmpArrayValue(*this);
621
10.8k
}
622
623
130
LangAltValue::LangAltValue() : XmpValue(langAlt) {
624
130
}
625
626
0
LangAltValue::LangAltValue(const std::string& buf) : XmpValue(langAlt) {
627
0
  read(buf);
628
0
}
629
630
0
int LangAltValue::read(const std::string& buf) {
631
0
  std::string b = buf;
632
0
  std::string lang = "x-default";
633
0
  if (buf.starts_with("lang=")) {
634
0
    static constexpr auto ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
635
636
0
    const std::string::size_type pos = buf.find_first_of(' ');
637
0
    if (pos == std::string::npos) {
638
0
      lang = buf.substr(5);
639
0
    } else {
640
0
      lang = buf.substr(5, pos - 5);
641
0
    }
642
0
    if (lang.empty())
643
0
      throw Error(ErrorCode::kerInvalidLangAltValue, buf);
644
    // Strip quotes (so you can also specify the language without quotes)
645
0
    if (lang.front() == '"') {
646
0
      lang = lang.substr(1);
647
648
0
      if (lang.empty() || lang.back() != '"')
649
0
        throw Error(ErrorCode::kerInvalidLangAltValue, buf);
650
651
0
      lang.pop_back();
652
0
    }
653
654
0
    if (lang.empty())
655
0
      throw Error(ErrorCode::kerInvalidLangAltValue, buf);
656
657
    // Check language is in the correct format (see https://www.ietf.org/rfc/rfc3066.txt)
658
0
    if (auto charPos = lang.find_first_not_of(ALPHA); charPos != std::string::npos) {
659
0
      static constexpr auto ALPHA_NUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
660
0
      if (lang.at(charPos) != '-' || lang.find_first_not_of(ALPHA_NUM, charPos + 1) != std::string::npos)
661
0
        throw Error(ErrorCode::kerInvalidLangAltValue, buf);
662
0
    }
663
664
0
    b.clear();
665
0
    if (pos != std::string::npos)
666
0
      b = buf.substr(pos + 1);
667
0
  }
668
669
0
  value_[lang] = std::move(b);
670
0
  return 0;
671
0
}
672
673
0
LangAltValue::UniquePtr LangAltValue::clone() const {
674
0
  return UniquePtr(clone_());
675
0
}
676
677
182
size_t LangAltValue::count() const {
678
182
  return value_.size();
679
182
}
680
681
182
std::ostream& LangAltValue::write(std::ostream& os) const {
682
182
  bool first = true;
683
684
  // Write the default entry first
685
182
  if (auto i = value_.find("x-default"); i != value_.end()) {
686
54
    os << "lang=\"" << i->first << "\" " << i->second;
687
54
    first = false;
688
54
  }
689
690
  // Write the others
691
190
  for (const auto& [lang, s] : value_) {
692
190
    if (lang != "x-default") {
693
136
      if (!first)
694
8
        os << ", ";
695
136
      os << "lang=\"" << lang << "\" " << s;
696
136
      first = false;
697
136
    }
698
190
  }
699
182
  return os;
700
182
}
701
702
0
std::string LangAltValue::toString(size_t /*n*/) const {
703
0
  return toString("x-default");
704
0
}
705
706
0
std::string LangAltValue::toString(const std::string& qualifier) const {
707
0
  if (auto i = value_.find(qualifier); i != value_.end()) {
708
0
    ok_ = true;
709
0
    return i->second;
710
0
  }
711
0
  ok_ = false;
712
0
  return "";
713
0
}
714
715
0
int64_t LangAltValue::toInt64(size_t /*n*/) const {
716
0
  ok_ = false;
717
0
  return 0;
718
0
}
719
720
0
uint32_t LangAltValue::toUint32(size_t /*n*/) const {
721
0
  ok_ = false;
722
0
  return 0;
723
0
}
724
725
0
float LangAltValue::toFloat(size_t /*n*/) const {
726
0
  ok_ = false;
727
0
  return 0.0F;
728
0
}
729
730
0
Rational LangAltValue::toRational(size_t /*n*/) const {
731
0
  ok_ = false;
732
0
  return {0, 0};
733
0
}
734
735
620
LangAltValue* LangAltValue::clone_() const {
736
620
  return new LangAltValue(*this);
737
620
}
738
739
768
DateValue::DateValue() : Value(date) {
740
768
  date_ = {};
741
768
}
742
743
0
DateValue::DateValue(int32_t year, int32_t month, int32_t day) : Value(date) {
744
0
  date_ = {year, month, day};
745
0
}
746
747
671
int DateValue::read(const byte* buf, size_t len, ByteOrder /*byteOrder*/) {
748
671
  const std::string str(reinterpret_cast<const char*>(buf), len);
749
671
  return read(str);
750
671
}
751
752
768
int DateValue::read(const std::string& buf) {
753
  // ISO 8601 date formats:
754
  // https://web.archive.org/web/20171020084445/https://www.loc.gov/standards/datetime/ISO_DIS%208601-1.pdf
755
768
  size_t monthPos = 0;
756
768
  size_t dayPos = 0;
757
758
907
  auto printWarning = [] {
759
907
#ifndef SUPPRESS_WARNINGS
760
907
    EXV_WARNING << Error(ErrorCode::kerUnsupportedDateFormat) << "\n";
761
907
#endif
762
907
  };
763
764
768
  if (buf.size() < 8) {
765
212
    printWarning();
766
212
    return 1;
767
212
  }
768
769
556
  if ((buf.size() >= 10 && buf[4] == '-' && buf[7] == '-') || (buf.size() == 8)) {
770
357
    if (buf.size() >= 10) {
771
16
      monthPos = 5;
772
16
      dayPos = 8;
773
341
    } else {
774
341
      monthPos = 4;
775
341
      dayPos = 6;
776
341
    }
777
778
771
    auto checkDigits = [&buf, &printWarning](size_t start, size_t count, int32_t& dest) {
779
2.52k
      for (size_t i = start; i < start + count; ++i) {
780
1.95k
        if (!std::isdigit(buf[i])) {
781
204
          printWarning();
782
204
          return 1;
783
204
        }
784
1.95k
      }
785
567
      dest = std::stoul(buf.substr(start, count));
786
567
      return 0;
787
771
    };
788
789
357
    if (checkDigits(0, 4, date_.year) || checkDigits(monthPos, 2, date_.month) || checkDigits(dayPos, 2, date_.day)) {
790
204
      printWarning();
791
204
      return 1;
792
204
    }
793
794
153
    if (date_.month > 12 || date_.day > 31) {
795
88
      date_.month = 0;
796
88
      date_.day = 0;
797
88
      printWarning();
798
88
      return 1;
799
88
    }
800
65
    return 0;
801
153
  }
802
199
  printWarning();
803
199
  return 1;
804
556
}
805
806
0
void DateValue::setDate(const Date& src) {
807
0
  date_ = src;
808
0
}
809
810
24
size_t DateValue::copy(byte* buf, ByteOrder /*byteOrder*/) const {
811
  // \note Here the date is copied in the Basic format YYYYMMDD, as the IPTC key  Iptc.Application2.DateCreated
812
  // wants it. Check https://exiv2.org/iptc.html
813
814
24
  auto out = reinterpret_cast<char*>(buf);
815
24
  auto it = stringFormatTo(out, "{:04}{:02}{:02}", date_.year, date_.month, date_.day);
816
817
24
  return it - out;
818
24
}
819
820
0
const DateValue::Date& DateValue::getDate() const {
821
0
  return date_;
822
0
}
823
824
0
size_t DateValue::count() const {
825
0
  return size();
826
0
}
827
828
48
size_t DateValue::size() const {
829
48
  return 8;
830
48
}
831
832
319
DateValue* DateValue::clone_() const {
833
319
  return new DateValue(*this);
834
319
}
835
836
262
std::ostream& DateValue::write(std::ostream& os) const {
837
  // Write DateValue in ISO 8601 Extended format: YYYY-MM-DD
838
262
  return os << stringFormat("{:04}-{:02}-{:02}", date_.year, date_.month, date_.day);
839
262
}
840
841
0
int64_t DateValue::toInt64(size_t /*n*/) const {
842
  // Range of tm struct is limited to about 1970 to 2038
843
  // This will return -1 if outside that range
844
0
  std::tm tms = {};
845
0
  tms.tm_mday = date_.day;
846
0
  tms.tm_mon = date_.month - 1;
847
0
  tms.tm_year = date_.year - 1900;
848
0
  auto l = static_cast<int64_t>(std::mktime(&tms));
849
0
  ok_ = (l != -1);
850
0
  return l;
851
0
}
852
853
0
uint32_t DateValue::toUint32(size_t /*n*/) const {
854
0
  const int64_t t = toInt64();
855
0
  if (t < 0 || t > std::numeric_limits<uint32_t>::max()) {
856
0
    ok_ = false;
857
0
    return 0;
858
0
  }
859
0
  return static_cast<uint32_t>(t);
860
0
}
861
862
0
float DateValue::toFloat(size_t n) const {
863
0
  return static_cast<float>(toInt64(n));
864
0
}
865
866
0
Rational DateValue::toRational(size_t n) const {
867
0
  const int64_t t = toInt64(n);
868
0
  if (t < std::numeric_limits<int32_t>::min() || t > std::numeric_limits<int32_t>::max()) {
869
0
    ok_ = false;
870
0
    return {0, 1};
871
0
  }
872
0
  return {static_cast<int32_t>(t), 1};
873
0
}
874
875
1.52k
TimeValue::TimeValue() : Value(time) {
876
1.52k
  time_ = {};
877
1.52k
}
878
879
0
TimeValue::TimeValue(int32_t hour, int32_t minute, int32_t second, int32_t tzHour, int32_t tzMinute) : Value(date) {
880
0
  time_ = {hour, minute, second, tzHour, tzMinute};
881
0
}
882
883
1.52k
int TimeValue::read(const byte* buf, size_t len, ByteOrder /*byteOrder*/) {
884
1.52k
  const std::string str(reinterpret_cast<const char*>(buf), len);
885
1.52k
  return read(str);
886
1.52k
}
887
888
1.52k
int TimeValue::read(const std::string& buf) {
889
  // ISO 8601 time formats:
890
  // https://web.archive.org/web/20171020084445/https://www.loc.gov/standards/datetime/ISO_DIS%208601-1.pdf
891
  // Not supported formats:
892
  // 4.2.2.4 Representations with decimal fraction: 232050,5
893
1.52k
  auto printWarning = [] {
894
1.11k
#ifndef SUPPRESS_WARNINGS
895
1.11k
    EXV_WARNING << Error(ErrorCode::kerUnsupportedTimeFormat) << "\n";
896
1.11k
#endif
897
1.11k
    return 1;
898
1.11k
  };
899
900
1.52k
  if (buf.size() < 2)
901
155
    return printWarning();
902
903
1.37k
  for (auto c : buf)
904
15.2k
    if (c != ':' && c != '+' && c != '-' && c != 'Z' && !std::isdigit(c))
905
558
      return printWarning();
906
907
814
  size_t mpos;
908
814
  size_t spos;
909
814
  if (buf.find(':') != std::string::npos) {
910
323
    mpos = 3;
911
323
    spos = 6;
912
491
  } else {
913
491
    mpos = 2;
914
491
    spos = 4;
915
491
  }
916
917
814
  auto hi = std::stoi(buf.substr(0, 2));
918
814
  if (hi < 0 || hi > 23)
919
144
    return printWarning();
920
670
  time_.hour = hi;
921
670
  if (buf.size() > 3) {
922
639
    auto mi = std::stoi(buf.substr(mpos, 2));
923
639
    if (mi < 0 || mi > 59)
924
53
      return printWarning();
925
586
    time_.minute = std::stoi(buf.substr(mpos, 2));
926
586
  } else {
927
31
    time_.minute = 0;
928
31
  }
929
617
  if (buf.size() > 5) {
930
539
    auto si = std::stoi(buf.substr(spos, 2));
931
539
    if (si < 0 || si > 60)
932
15
      return printWarning();
933
524
    time_.second = std::stoi(buf.substr(spos, 2));
934
524
  } else {
935
78
    time_.second = 0;
936
78
  }
937
938
602
  auto fpos = buf.find('+');
939
602
  if (fpos == std::string::npos)
940
475
    fpos = buf.find('-');
941
942
602
  if (fpos != std::string::npos) {
943
454
    auto format = buf.substr(fpos, buf.size());
944
454
    auto posColon = format.find(':');
945
454
    if (posColon == std::string::npos) {
946
      // Extended format
947
264
      auto tzhi = std::stoi(format.substr(0, 3));
948
264
      if (tzhi < -23 || tzhi > 23)
949
12
        return printWarning();
950
252
      time_.tzHour = tzhi;
951
252
      if (format.size() > 3) {
952
208
        int minute = std::stoi(format.substr(3));
953
208
        if (minute < 0 || minute > 59)
954
38
          return printWarning();
955
170
        time_.tzMinute = time_.tzHour < 0 ? -minute : minute;
956
170
      }
957
252
    } else {
958
      // Basic format
959
190
      auto tzhi = std::stoi(format.substr(0, posColon));
960
190
      if (tzhi < -23 || tzhi > 23)
961
69
        return printWarning();
962
121
      time_.tzHour = tzhi;
963
121
      int minute = std::stoi(format.substr(posColon + 1));
964
121
      if (minute < 0 || minute > 59)
965
69
        return printWarning();
966
52
      time_.tzMinute = time_.tzHour < 0 ? -minute : minute;
967
52
    }
968
454
  }
969
414
  return 0;
970
602
}
971
972
/// \todo not used internally. At least we should test it
973
0
void TimeValue::setTime(const Time& src) {
974
0
  time_ = src;
975
0
}
976
977
8
size_t TimeValue::copy(byte* buf, ByteOrder /*byteOrder*/) const {
978
  // NOTE: Here the time is copied in the Basic format HHMMSS:HHMM, as the IPTC key
979
  // Iptc.Application2.TimeCreated wants it. Check https://exiv2.org/iptc.html
980
8
  char plusMinus = '+';
981
8
  if (time_.tzHour < 0 || time_.tzMinute < 0)
982
5
    plusMinus = '-';
983
984
8
  auto out = reinterpret_cast<char*>(buf);
985
8
  auto it = stringFormatTo(out, "{:02}{:02}{:02}{}{:02}{:02}", time_.hour, time_.minute, time_.second, plusMinus,
986
8
                           std::abs(time_.tzHour), std::abs(time_.tzMinute));
987
988
8
  auto wrote = static_cast<size_t>(it - out);
989
8
  Internal::enforce(wrote == 11, Exiv2::ErrorCode::kerUnsupportedTimeFormat);
990
8
  return wrote;
991
8
}
992
993
0
const TimeValue::Time& TimeValue::getTime() const {
994
0
  return time_;
995
0
}
996
997
0
size_t TimeValue::count() const {
998
0
  return size();
999
0
}
1000
1001
16
size_t TimeValue::size() const {
1002
16
  return 11;
1003
16
}
1004
1005
527
TimeValue* TimeValue::clone_() const {
1006
527
  return new TimeValue(*this);
1007
527
}
1008
1009
94
std::ostream& TimeValue::write(std::ostream& os) const {
1010
  // Write TimeValue in ISO 8601 Extended format: hh:mm:ss±hh:mm
1011
94
  char plusMinus = '+';
1012
94
  if (time_.tzHour < 0 || time_.tzMinute < 0)
1013
36
    plusMinus = '-';
1014
1015
94
  return os << stringFormat("{:02}:{:02}:{:02}{}{:02}:{:02}", time_.hour, time_.minute, time_.second, plusMinus,
1016
94
                            std::abs(time_.tzHour), std::abs(time_.tzMinute));
1017
94
}
1018
1019
0
int64_t TimeValue::toInt64(size_t /*n*/) const {
1020
  // Returns number of seconds in the day in UTC.
1021
0
  auto result = static_cast<int64_t>(time_.hour - time_.tzHour) * 60 * 60;
1022
0
  result += static_cast<int64_t>(time_.minute - time_.tzMinute) * 60;
1023
0
  result += time_.second;
1024
0
  if (result < 0) {
1025
0
    result += 86400;
1026
0
  }
1027
0
  ok_ = true;
1028
0
  return result;
1029
0
}
1030
1031
0
uint32_t TimeValue::toUint32(size_t /*n*/) const {
1032
0
  return static_cast<uint32_t>(std::clamp<int64_t>(toInt64(), 0, std::numeric_limits<uint32_t>::max()));
1033
0
}
1034
1035
0
float TimeValue::toFloat(size_t n) const {
1036
0
  return static_cast<float>(toInt64(n));
1037
0
}
1038
1039
0
Rational TimeValue::toRational(size_t n) const {
1040
0
  return {static_cast<int32_t>(toInt64(n)), 1};
1041
0
}
1042
1043
}  // namespace Exiv2