Skip to content

Commit 46e11c5

Browse files
authored
Add support for file creation/modification dates. Closes: #1701
See also: PR #1705
1 parent 4462f3e commit 46e11c5

12 files changed

Lines changed: 282 additions & 11 deletions

File tree

src/libaudcore/audstrings.cc

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,21 @@ static unsigned str_to_uint(const char * string, const char ** end = nullptr,
10421042
return val;
10431043
}
10441044

1045-
static int digits_for(unsigned val)
1045+
static uint64_t str_to_uint64(const char * string, const char ** end = nullptr,
1046+
const char * stop = nullptr)
1047+
{
1048+
uint64_t val = 0;
1049+
1050+
for (char c; string != stop && (c = *string) >= '0' && c <= '9'; string++)
1051+
val = val * 10 + (c - '0');
1052+
1053+
if (end)
1054+
*end = string;
1055+
1056+
return val;
1057+
}
1058+
1059+
static int digits_for(uint64_t val)
10461060
{
10471061
int digits = 1;
10481062

@@ -1060,6 +1074,12 @@ static void uint_to_str(unsigned val, char * buf, int digits)
10601074
*(--rev) = '0' + val % 10;
10611075
}
10621076

1077+
static void uint64_to_str(uint64_t val, char * buf, int digits)
1078+
{
1079+
for (char * rev = buf + digits; rev > buf; val /= 10)
1080+
*(--rev) = '0' + val % 10;
1081+
}
1082+
10631083
EXPORT int str_to_int(const char * string)
10641084
{
10651085
bool neg = (string[0] == '-');
@@ -1070,6 +1090,16 @@ EXPORT int str_to_int(const char * string)
10701090
return neg ? -val : val;
10711091
}
10721092

1093+
EXPORT int64_t str_to_int64(const char * string)
1094+
{
1095+
bool neg = (string[0] == '-');
1096+
if (neg || string[0] == '+')
1097+
string++;
1098+
1099+
uint64_t val = str_to_uint64(string);
1100+
return neg ? -val : val;
1101+
}
1102+
10731103
EXPORT double str_to_double(const char * string)
10741104
{
10751105
bool neg = (string[0] == '-');
@@ -1104,6 +1134,21 @@ EXPORT void str_insert_int(StringBuf & string, int pos, int val)
11041134
uint_to_str(absval, set, digits);
11051135
}
11061136

1137+
EXPORT void str_insert_int64(StringBuf & string, int pos, int64_t val)
1138+
{
1139+
bool neg = (val < 0);
1140+
uint64_t absval = neg ? -val : val;
1141+
1142+
int digits = digits_for(absval);
1143+
int len = (neg ? 1 : 0) + digits;
1144+
char * set = string.insert(pos, nullptr, len);
1145+
1146+
if (neg)
1147+
*(set++) = '-';
1148+
1149+
uint64_to_str(absval, set, digits);
1150+
}
1151+
11071152
EXPORT void str_insert_double(StringBuf & string, int pos, double val)
11081153
{
11091154
bool neg = (val < 0);
@@ -1147,6 +1192,13 @@ EXPORT StringBuf int_to_str(int val)
11471192
return buf;
11481193
}
11491194

1195+
EXPORT StringBuf int64_to_str(int64_t val)
1196+
{
1197+
StringBuf buf;
1198+
str_insert_int64(buf, 0, val);
1199+
return buf;
1200+
}
1201+
11501202
EXPORT StringBuf double_to_str(double val)
11511203
{
11521204
StringBuf buf;

src/libaudcore/audstrings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,12 @@ Index<String> str_list_to_index(const char * list, const char * delims);
113113
StringBuf index_to_str_list(const Index<String> & index, const char * sep);
114114

115115
int str_to_int(const char * string);
116+
int64_t str_to_int64(const char * string);
116117
double str_to_double(const char * string);
117118
void str_insert_int(StringBuf & string, int pos, int val);
118119
void str_insert_double(StringBuf & string, int pos, double val);
119120
StringBuf int_to_str(int val);
121+
StringBuf int64_to_str(int64_t val);
120122
StringBuf double_to_str(double val);
121123

122124
bool str_to_int_array(const char * string, int * array, int count);

src/libaudcore/playlist-utils.cc

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,16 @@ static int tuple_compare_string(const Tuple & a, const Tuple & b,
7070
static int tuple_compare_int(const Tuple & a, const Tuple & b,
7171
Tuple::Field field)
7272
{
73-
if (a.get_value_type(field) != Tuple::Int)
74-
return (b.get_value_type(field) != Tuple::Int) ? 0 : -1;
75-
if (b.get_value_type(field) != Tuple::Int)
73+
auto at = a.get_value_type(field);
74+
auto bt = a.get_value_type(field);
75+
76+
if (at != Tuple::Int && at != Tuple::DateTime)
77+
return (bt != Tuple::Int && bt != Tuple::DateTime) ? 0 : -1;
78+
if (bt != Tuple::Int && bt != Tuple::DateTime)
7679
return 1;
7780

78-
int int_a = a.get_int(field);
79-
int int_b = b.get_int(field);
81+
int64_t int_a = a.get_int64(field);
82+
int64_t int_b = b.get_int64(field);
8083

8184
return (int_a < int_b) ? -1 : (int_a > int_b);
8285
}
@@ -133,6 +136,14 @@ static int tuple_compare_disc(const Tuple & a, const Tuple & b)
133136
{
134137
return tuple_compare_int(a, b, Tuple::Disc);
135138
}
139+
static int tuple_compare_created(const Tuple & a, const Tuple & b)
140+
{
141+
return tuple_compare_int(a, b, Tuple::FileCreated);
142+
}
143+
static int tuple_compare_modified(const Tuple & a, const Tuple & b)
144+
{
145+
return tuple_compare_int(a, b, Tuple::FileModified);
146+
}
136147

137148

138149
static const Playlist::StringCompareFunc filename_comparisons[] = {
@@ -150,7 +161,9 @@ static const Playlist::StringCompareFunc filename_comparisons[] = {
150161
nullptr, // comment
151162
nullptr, // publisher
152163
nullptr, // catalog number
153-
nullptr // disc number
164+
nullptr, // disc number
165+
nullptr, // created
166+
nullptr // modified
154167
};
155168

156169
static const Playlist::TupleCompareFunc tuple_comparisons[] = {
@@ -168,7 +181,9 @@ static const Playlist::TupleCompareFunc tuple_comparisons[] = {
168181
tuple_compare_comment,
169182
tuple_compare_publisher,
170183
tuple_compare_catalog_number,
171-
tuple_compare_disc};
184+
tuple_compare_disc,
185+
tuple_compare_created,
186+
tuple_compare_modified};
172187

173188
static_assert(aud::n_elems(filename_comparisons) == Playlist::n_sort_types &&
174189
aud::n_elems(tuple_comparisons) == Playlist::n_sort_types,

src/libaudcore/playlist.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class Playlist
7979
Publisher,
8080
CatalogNum,
8181
Disc,
82+
FileCreated,
83+
FileModified,
8284
n_sort_types
8385
};
8486

src/libaudcore/probe.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "plugin.h"
2929
#include "plugins-internal.h"
3030
#include "runtime.h"
31+
#include "vfs.h"
3132

3233
bool open_input_file(const char * filename, const char * mode, InputPlugin * ip,
3334
VFSFile & file, String * error)
@@ -210,6 +211,16 @@ EXPORT bool aud_file_read_tag(const char * filename, PluginHandle * decoder,
210211
{
211212
// cleanly replace existing tuple
212213
new_tuple.set_state(Tuple::Valid);
214+
215+
int64_t m = -1, c = -1;
216+
if (VFSFile::get_file_timestamps(filename, &m, &c))
217+
{
218+
if (m > 0)
219+
new_tuple.set_int64(Tuple::FileModified, m);
220+
if (c > 0)
221+
new_tuple.set_int64(Tuple::FileCreated, c);
222+
}
223+
213224
tuple = std::move(new_tuple);
214225
return true;
215226
}

src/libaudcore/tuple.cc

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static_assert(n_private_fields <= 64,
4646

4747
union TupleVal {
4848
String str;
49-
int x;
49+
int64_t x;
5050

5151
// dummy constructor and destructor
5252
TupleVal() {}
@@ -85,6 +85,7 @@ struct TupleData
8585
TupleVal * lookup(int field, bool add, bool remove);
8686
void set_int(int field, int x);
8787
void set_str(int field, const char * str);
88+
void set_int64(int field, int64_t x);
8889
void set_subtunes(short nsubs, const short * subs);
8990

9091
static TupleData * ref(TupleData * tuple);
@@ -153,6 +154,9 @@ static const struct
153154

154155
{"formatted-title", Tuple::String, -1},
155156

157+
{"file-created", Tuple::DateTime, -1},
158+
{"file-modified", Tuple::DateTime, -1},
159+
156160
/* fallbacks */
157161
{nullptr, Tuple::String, -1},
158162
{nullptr, Tuple::String, -1},
@@ -184,7 +188,9 @@ static const FieldDictEntry field_dict[] = {
184188
{"date", Tuple::Date},
185189
{"description", Tuple::Description},
186190
{"disc-number", Tuple::Disc},
191+
{"file-created", Tuple::FileCreated},
187192
{"file-ext", Tuple::Suffix},
193+
{"file-modified", Tuple::FileModified},
188194
{"file-name", Tuple::Basename},
189195
{"file-path", Tuple::Path},
190196
{"formatted-title", Tuple::FormattedTitle},
@@ -296,6 +302,12 @@ void TupleData::set_str(int field, const char * str)
296302
new (&val->str) String(str);
297303
}
298304

305+
void TupleData::set_int64(int field, int64_t x)
306+
{
307+
TupleVal * val = lookup(field, true, false);
308+
val->x = x;
309+
}
310+
299311
void TupleData::set_subtunes(short nsubs, const short * subs)
300312
{
301313
nsubtunes = nsubs;
@@ -473,6 +485,14 @@ EXPORT int Tuple::get_int(Field field) const
473485
return val ? val->x : -1;
474486
}
475487

488+
EXPORT int64_t Tuple::get_int64(Field field) const
489+
{
490+
assert(is_valid_field(field) && (field_info[field].type == Int || field_info[field].type == DateTime));
491+
492+
TupleVal * val = data ? data->lookup(field, false, false) : nullptr;
493+
return val ? val->x : -1;
494+
}
495+
476496
EXPORT String Tuple::get_str(Field field) const
477497
{
478498
assert(is_valid_field(field) && field_info[field].type == String);
@@ -489,6 +509,14 @@ EXPORT void Tuple::set_int(Field field, int x)
489509
data->set_int(field, x);
490510
}
491511

512+
EXPORT void Tuple::set_int64(Field field, int64_t x)
513+
{
514+
assert(is_valid_field(field) && (field_info[field].type == Int || field_info[field].type == DateTime));
515+
516+
data = TupleData::copy_on_write(data);
517+
data->set_int64(field, x);
518+
}
519+
492520
EXPORT void Tuple::set_str(Field field, const char * str)
493521
{
494522
assert(is_valid_field(field) && field_info[field].type == String);

src/libaudcore/tuple.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#ifndef LIBAUDCORE_TUPLE_H
2727
#define LIBAUDCORE_TUPLE_H
2828

29+
#include <stdint.h>
30+
2931
#include <libaudcore/objects.h>
3032

3133
struct ReplayGainInfo;
@@ -109,6 +111,9 @@ class Tuple
109111
field */
110112
FormattedTitle,
111113

114+
FileCreated, /* File creation datetime (Unix epoch) */
115+
FileModified, /* File modification datetime (Unix epoch) */
116+
112117
n_fields
113118
};
114119

@@ -118,6 +123,7 @@ class Tuple
118123
{
119124
String,
120125
Int,
126+
DateTime,
121127
Empty
122128
};
123129

@@ -161,6 +167,9 @@ class Tuple
161167
/* Returns the string value of a field if set, otherwise null. */
162168
::String get_str(Field field) const;
163169

170+
/* Returns the int64 value of a field if set, otherwise -1. */
171+
int64_t get_int64(Field field) const;
172+
164173
/* Sets a field to the integer value <x>. */
165174
void set_int(Field field, int x);
166175

@@ -169,6 +178,9 @@ class Tuple
169178
* Equivalent to unset() if <str> is null. */
170179
void set_str(Field field, const char * str);
171180

181+
/* Sets a field to the int64 value <x>. */
182+
void set_int64(Field field, int64_t x);
183+
172184
/* Clears any value that a field is currently set to. */
173185
void unset(Field field);
174186

src/libaudcore/vfs.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,15 @@ EXPORT Index<const char *> VFSFile::supported_uri_schemes()
446446

447447
return schemes;
448448
}
449+
450+
EXPORT bool VFSFile::get_file_timestamps(const char * filename, int64_t * mtime,
451+
int64_t * birthtime)
452+
{
453+
// Check if it's a local file
454+
StringBuf scheme = uri_get_scheme(filename);
455+
if (!scheme || strcmp(scheme, "file") != 0)
456+
return false;
457+
458+
// Use the static local_transport instance
459+
return local_transport.get_file_timestamps(filename, mtime, birthtime);
460+
}

src/libaudcore/vfs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ class VFSFile
188188
/* returns a list of supported URI schemes */
189189
static Index<const char *> supported_uri_schemes();
190190

191+
/* Get file modification and creation times (for local files only)
192+
* Returns true on success, false if not a local file or error */
193+
static bool get_file_timestamps(const char * filename, int64_t * mtime,
194+
int64_t * birthtime);
195+
191196
private:
192197
String m_filename, m_error;
193198
SmartPtr<VFSImpl> m_impl;

0 commit comments

Comments
 (0)