Merged release 0.15.2 from branch 'v0.15.x'
Conflicts: NEWS configure.ac
This commit is contained in:
commit
f401c1059c
16
NEWS
16
NEWS
@ -27,6 +27,22 @@ ver 0.16 (20??/??/??)
|
|||||||
* obey $(sysconfdir) for default mpd.conf location
|
* obey $(sysconfdir) for default mpd.conf location
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.2 (2009/08/15)
|
||||||
|
* tags:
|
||||||
|
- ape: check the tag size (fixes integer underflow)
|
||||||
|
- ape: added protection against large memory allocations
|
||||||
|
* decoders:
|
||||||
|
- mad: skip ID3 frames when libid3tag is disabled
|
||||||
|
- flac: parse all replaygain tags
|
||||||
|
- flac: don't allocate cuesheet twice (memleak)
|
||||||
|
* output:
|
||||||
|
- shout: fixed stuck pause bug
|
||||||
|
- shout: minimize the unpause latency
|
||||||
|
* update: free empty path string (memleak)
|
||||||
|
* update: free temporary string in container scan (memleak)
|
||||||
|
* directory: free empty directories after removing them (memleak)
|
||||||
|
|
||||||
|
|
||||||
ver 0.15.1 (2009/07/15)
|
ver 0.15.1 (2009/07/15)
|
||||||
* decoders:
|
* decoders:
|
||||||
- flac: fix assertion failure in tag_free() call
|
- flac: fix assertion failure in tag_free() call
|
||||||
|
@ -629,8 +629,7 @@ dnl audio output plugins
|
|||||||
dnl
|
dnl
|
||||||
|
|
||||||
AC_ARG_ENABLE(alsa,
|
AC_ARG_ENABLE(alsa,
|
||||||
AS_HELP_STRING([--enable-alsa],
|
AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
|
||||||
[disable ALSA support]),,
|
|
||||||
enable_alsa=auto)
|
enable_alsa=auto)
|
||||||
|
|
||||||
AC_ARG_ENABLE(ao,
|
AC_ARG_ENABLE(ao,
|
||||||
|
@ -40,53 +40,57 @@ flac_data_init(struct flac_data *data, struct decoder * decoder,
|
|||||||
data->tag = NULL;
|
data->tag = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
flac_find_float_comment(const FLAC__StreamMetadata *block,
|
flac_find_float_comment(const FLAC__StreamMetadata *block,
|
||||||
const char *cmnt, float *fl)
|
const char *cmnt, float *fl, bool *found_r)
|
||||||
{
|
{
|
||||||
int offset =
|
int offset;
|
||||||
FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, cmnt);
|
size_t pos;
|
||||||
|
int len;
|
||||||
|
unsigned char tmp, *p;
|
||||||
|
|
||||||
if (offset >= 0) {
|
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
|
||||||
size_t pos = strlen(cmnt) + 1; /* 1 is for '=' */
|
cmnt);
|
||||||
int len = block->data.vorbis_comment.comments[offset].length
|
if (offset < 0)
|
||||||
- pos;
|
return;
|
||||||
if (len > 0) {
|
|
||||||
unsigned char tmp;
|
|
||||||
unsigned char *p = &(block->data.vorbis_comment.
|
|
||||||
comments[offset].entry[pos]);
|
|
||||||
tmp = p[len];
|
|
||||||
p[len] = '\0';
|
|
||||||
*fl = (float)atof((char *)p);
|
|
||||||
p[len] = tmp;
|
|
||||||
|
|
||||||
return true;
|
pos = strlen(cmnt) + 1; /* 1 is for '=' */
|
||||||
}
|
len = block->data.vorbis_comment.comments[offset].length - pos;
|
||||||
}
|
if (len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
return false;
|
p = &block->data.vorbis_comment.comments[offset].entry[pos];
|
||||||
|
tmp = p[len];
|
||||||
|
p[len] = '\0';
|
||||||
|
*fl = (float)atof((char *)p);
|
||||||
|
p[len] = tmp;
|
||||||
|
|
||||||
|
*found_r = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* replaygain stuff by AliasMrJones */
|
|
||||||
static void
|
static void
|
||||||
flac_parse_replay_gain(const FLAC__StreamMetadata *block,
|
flac_parse_replay_gain(const FLAC__StreamMetadata *block,
|
||||||
struct flac_data *data)
|
struct flac_data *data)
|
||||||
{
|
{
|
||||||
bool found;
|
bool found = false;
|
||||||
|
|
||||||
if (data->replay_gain_info)
|
if (data->replay_gain_info)
|
||||||
replay_gain_info_free(data->replay_gain_info);
|
replay_gain_info_free(data->replay_gain_info);
|
||||||
|
|
||||||
data->replay_gain_info = replay_gain_info_new();
|
data->replay_gain_info = replay_gain_info_new();
|
||||||
|
|
||||||
found = flac_find_float_comment(block, "replaygain_album_gain",
|
flac_find_float_comment(block, "replaygain_album_gain",
|
||||||
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain) ||
|
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain,
|
||||||
flac_find_float_comment(block, "replaygain_album_peak",
|
&found);
|
||||||
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak) ||
|
flac_find_float_comment(block, "replaygain_album_peak",
|
||||||
flac_find_float_comment(block, "replaygain_track_gain",
|
&data->replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak,
|
||||||
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain) ||
|
&found);
|
||||||
flac_find_float_comment(block, "replaygain_track_peak",
|
flac_find_float_comment(block, "replaygain_track_gain",
|
||||||
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak);
|
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain,
|
||||||
|
&found);
|
||||||
|
flac_find_float_comment(block, "replaygain_track_peak",
|
||||||
|
&data->replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak,
|
||||||
|
&found);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
replay_gain_info_free(data->replay_gain_info);
|
replay_gain_info_free(data->replay_gain_info);
|
||||||
@ -106,25 +110,27 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
|||||||
size_t char_tnum_length = 0;
|
size_t char_tnum_length = 0;
|
||||||
const char *comment = (const char*)entry->entry;
|
const char *comment = (const char*)entry->entry;
|
||||||
|
|
||||||
if (entry->length > name_length &&
|
if (entry->length <= name_length ||
|
||||||
g_ascii_strncasecmp(comment, name, name_length) == 0) {
|
g_ascii_strncasecmp(comment, name, name_length) != 0)
|
||||||
if (char_tnum != NULL) {
|
return NULL;
|
||||||
char_tnum_length = strlen(char_tnum);
|
|
||||||
if (entry->length > name_length + char_tnum_length + 2 &&
|
if (char_tnum != NULL) {
|
||||||
comment[name_length] == '[' &&
|
char_tnum_length = strlen(char_tnum);
|
||||||
g_ascii_strncasecmp(comment + name_length + 1,
|
if (entry->length > name_length + char_tnum_length + 2 &&
|
||||||
char_tnum, char_tnum_length) == 0 &&
|
comment[name_length] == '[' &&
|
||||||
comment[name_length + char_tnum_length + 1] == ']')
|
g_ascii_strncasecmp(comment + name_length + 1,
|
||||||
name_length = name_length + char_tnum_length + 2;
|
char_tnum, char_tnum_length) == 0 &&
|
||||||
else if (entry->length > name_length + char_tnum_length &&
|
comment[name_length + char_tnum_length + 1] == ']')
|
||||||
g_ascii_strncasecmp(comment + name_length,
|
name_length = name_length + char_tnum_length + 2;
|
||||||
char_tnum, char_tnum_length) == 0)
|
else if (entry->length > name_length + char_tnum_length &&
|
||||||
name_length = name_length + char_tnum_length;
|
g_ascii_strncasecmp(comment + name_length,
|
||||||
}
|
char_tnum, char_tnum_length) == 0)
|
||||||
if (comment[name_length] == '=') {
|
name_length = name_length + char_tnum_length;
|
||||||
*length_r = entry->length - name_length - 1;
|
}
|
||||||
return comment + name_length + 1;
|
|
||||||
}
|
if (comment[name_length] == '=') {
|
||||||
|
*length_r = entry->length - name_length - 1;
|
||||||
|
return comment + name_length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -370,13 +376,15 @@ char*
|
|||||||
flac_cue_track( const char* pathname,
|
flac_cue_track( const char* pathname,
|
||||||
const unsigned int tnum)
|
const unsigned int tnum)
|
||||||
{
|
{
|
||||||
FLAC__StreamMetadata* cs = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET);
|
FLAC__bool success;
|
||||||
|
FLAC__StreamMetadata* cs;
|
||||||
|
|
||||||
FLAC__metadata_get_cuesheet(pathname, &cs);
|
success = FLAC__metadata_get_cuesheet(pathname, &cs);
|
||||||
|
if (!success)
|
||||||
if (cs == NULL)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
assert(cs != NULL);
|
||||||
|
|
||||||
if (cs->data.cue_sheet.num_tracks <= 1)
|
if (cs->data.cue_sheet.num_tracks <= 1)
|
||||||
{
|
{
|
||||||
FLAC__metadata_object_delete(cs);
|
FLAC__metadata_object_delete(cs);
|
||||||
|
@ -350,11 +350,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_ID3TAG
|
|
||||||
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
|
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
|
||||||
struct tag **mpd_tag,
|
struct tag **mpd_tag,
|
||||||
struct replay_gain_info **replay_gain_info_r)
|
struct replay_gain_info **replay_gain_info_r)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_ID3TAG
|
||||||
struct id3_tag *id3_tag = NULL;
|
struct id3_tag *id3_tag = NULL;
|
||||||
id3_length_t count;
|
id3_length_t count;
|
||||||
id3_byte_t const *id3_data;
|
id3_byte_t const *id3_data;
|
||||||
@ -418,8 +418,34 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
|
|||||||
id3_tag_delete(id3_tag);
|
id3_tag_delete(id3_tag);
|
||||||
|
|
||||||
g_free(allocated);
|
g_free(allocated);
|
||||||
}
|
#else /* !HAVE_ID3TAG */
|
||||||
|
(void)mpd_tag;
|
||||||
|
(void)replay_gain_info_r;
|
||||||
|
|
||||||
|
/* This code is enabled when libid3tag is disabled. Instead
|
||||||
|
of parsing the ID3 frame, it just skips it. */
|
||||||
|
|
||||||
|
mad_stream_skip(&data->stream, tagsize);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_ID3TAG
|
||||||
|
/**
|
||||||
|
* This function emulates libid3tag when it is disabled. Instead of
|
||||||
|
* doing a real analyzation of the frame, it just checks whether the
|
||||||
|
* frame begins with the string "ID3". If so, it returns the full
|
||||||
|
* length.
|
||||||
|
*/
|
||||||
|
static signed long
|
||||||
|
id3_tag_query(const void *p0, size_t length)
|
||||||
|
{
|
||||||
|
const char *p = p0;
|
||||||
|
|
||||||
|
return length > 3 && memcmp(p, "ID3", 3) == 0
|
||||||
|
? length
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
#endif /* !HAVE_ID3TAG */
|
||||||
|
|
||||||
static enum mp3_action
|
static enum mp3_action
|
||||||
decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag,
|
decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag,
|
||||||
@ -433,7 +459,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag,
|
|||||||
return DECODE_BREAK;
|
return DECODE_BREAK;
|
||||||
}
|
}
|
||||||
if (mad_header_decode(&data->frame.header, &data->stream)) {
|
if (mad_header_decode(&data->frame.header, &data->stream)) {
|
||||||
#ifdef HAVE_ID3TAG
|
|
||||||
if ((data->stream).error == MAD_ERROR_LOSTSYNC &&
|
if ((data->stream).error == MAD_ERROR_LOSTSYNC &&
|
||||||
(data->stream).this_frame) {
|
(data->stream).this_frame) {
|
||||||
signed long tagsize = id3_tag_query((data->stream).
|
signed long tagsize = id3_tag_query((data->stream).
|
||||||
@ -454,7 +479,6 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag,
|
|||||||
return DECODE_CONT;
|
return DECODE_CONT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
if (MAD_RECOVERABLE((data->stream).error)) {
|
if (MAD_RECOVERABLE((data->stream).error)) {
|
||||||
return DECODE_SKIP;
|
return DECODE_SKIP;
|
||||||
} else {
|
} else {
|
||||||
@ -493,7 +517,6 @@ decodeNextFrame(struct mp3_data *data)
|
|||||||
return DECODE_BREAK;
|
return DECODE_BREAK;
|
||||||
}
|
}
|
||||||
if (mad_frame_decode(&data->frame, &data->stream)) {
|
if (mad_frame_decode(&data->frame, &data->stream)) {
|
||||||
#ifdef HAVE_ID3TAG
|
|
||||||
if ((data->stream).error == MAD_ERROR_LOSTSYNC) {
|
if ((data->stream).error == MAD_ERROR_LOSTSYNC) {
|
||||||
signed long tagsize = id3_tag_query((data->stream).
|
signed long tagsize = id3_tag_query((data->stream).
|
||||||
this_frame,
|
this_frame,
|
||||||
@ -506,7 +529,6 @@ decodeNextFrame(struct mp3_data *data)
|
|||||||
return DECODE_CONT;
|
return DECODE_CONT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
if (MAD_RECOVERABLE((data->stream).error)) {
|
if (MAD_RECOVERABLE((data->stream).error)) {
|
||||||
return DECODE_SKIP;
|
return DECODE_SKIP;
|
||||||
} else {
|
} else {
|
||||||
|
@ -73,9 +73,14 @@ directory_prune_empty(struct directory *directory)
|
|||||||
struct dirvec *dv = &directory->children;
|
struct dirvec *dv = &directory->children;
|
||||||
|
|
||||||
for (i = dv->nr; --i >= 0; ) {
|
for (i = dv->nr; --i >= 0; ) {
|
||||||
directory_prune_empty(dv->base[i]);
|
struct directory *child = dv->base[i];
|
||||||
if (directory_is_empty(dv->base[i]))
|
|
||||||
dirvec_delete(dv, dv->base[i]);
|
directory_prune_empty(child);
|
||||||
|
|
||||||
|
if (directory_is_empty(child)) {
|
||||||
|
dirvec_delete(dv, child);
|
||||||
|
directory_free(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!dv->nr)
|
if (!dv->nr)
|
||||||
dirvec_destroy(dv);
|
dirvec_destroy(dv);
|
||||||
|
@ -448,8 +448,15 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error)
|
|||||||
static bool
|
static bool
|
||||||
my_shout_pause(void *data)
|
my_shout_pause(void *data)
|
||||||
{
|
{
|
||||||
|
struct shout_data *sd = (struct shout_data *)data;
|
||||||
static const char silence[1020];
|
static const char silence[1020];
|
||||||
|
|
||||||
|
if (shout_delay(sd->shout_conn) > 500) {
|
||||||
|
/* cap the latency for unpause */
|
||||||
|
g_usleep(500000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return my_shout_play(data, silence, sizeof(silence), NULL);
|
return my_shout_play(data, silence, sizeof(silence), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,17 @@ audio_output_open(struct audio_output *ao,
|
|||||||
audio_format_equals(audio_format, &ao->in_audio_format)) {
|
audio_format_equals(audio_format, &ao->in_audio_format)) {
|
||||||
assert(ao->pipe == mp);
|
assert(ao->pipe == mp);
|
||||||
|
|
||||||
|
if (ao->pause) {
|
||||||
|
/* unpause with the CANCEL command; this is a
|
||||||
|
hack, but suits well for forcing the thread
|
||||||
|
to leave the ao_pause() thread, and we need
|
||||||
|
to flush the device buffer anyway */
|
||||||
|
|
||||||
|
/* we're not using audio_output_cancel() here,
|
||||||
|
because that function is asynchronous */
|
||||||
|
ao_command(ao, AO_COMMAND_CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,12 @@ struct audio_output {
|
|||||||
*/
|
*/
|
||||||
bool open;
|
bool open;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the device paused? i.e. the output thread is in the
|
||||||
|
* ao_pause() loop.
|
||||||
|
*/
|
||||||
|
bool pause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If not NULL, the device has failed, and this timer is used
|
* If not NULL, the device has failed, and this timer is used
|
||||||
* to estimate how long it should stay disabled (unless
|
* to estimate how long it should stay disabled (unless
|
||||||
|
@ -295,6 +295,7 @@ static void ao_pause(struct audio_output *ao)
|
|||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
ao_plugin_cancel(ao->plugin, ao->data);
|
ao_plugin_cancel(ao->plugin, ao->data);
|
||||||
|
ao->pause = true;
|
||||||
ao_command_finished(ao);
|
ao_command_finished(ao);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@ -304,6 +305,8 @@ static void ao_pause(struct audio_output *ao)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (ao->command == AO_COMMAND_NONE);
|
} while (ao->command == AO_COMMAND_NONE);
|
||||||
|
|
||||||
|
ao->pause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer audio_output_task(gpointer arg)
|
static gpointer audio_output_task(gpointer arg)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static const char *const ape_tag_names[] = {
|
static const char *const ape_tag_names[] = {
|
||||||
@ -95,15 +96,18 @@ tag_ape_load(const char *file)
|
|||||||
|
|
||||||
/* find beginning of ape tag */
|
/* find beginning of ape tag */
|
||||||
tagLen = GUINT32_FROM_LE(footer.length);
|
tagLen = GUINT32_FROM_LE(footer.length);
|
||||||
if (tagLen < sizeof(footer))
|
if (tagLen <= sizeof(footer) + 10)
|
||||||
|
goto fail;
|
||||||
|
if (tagLen > 1024 * 1024)
|
||||||
|
/* refuse to load more than one megabyte of tag data */
|
||||||
goto fail;
|
goto fail;
|
||||||
if (fseek(fp, size - tagLen, SEEK_SET))
|
if (fseek(fp, size - tagLen, SEEK_SET))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/* read tag into buffer */
|
/* read tag into buffer */
|
||||||
tagLen -= sizeof(footer);
|
tagLen -= sizeof(footer);
|
||||||
if (tagLen <= 0)
|
assert(tagLen > 10);
|
||||||
goto fail;
|
|
||||||
buffer = g_malloc(tagLen);
|
buffer = g_malloc(tagLen);
|
||||||
if (fread(buffer, 1, tagLen, fp) != tagLen)
|
if (fread(buffer, 1, tagLen, fp) != tagLen)
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -121,7 +125,7 @@ tag_ape_load(const char *file)
|
|||||||
|
|
||||||
/* get the key */
|
/* get the key */
|
||||||
key = p;
|
key = p;
|
||||||
while (tagLen - size > 0 && *p != '\0') {
|
while (tagLen > size && *p != '\0') {
|
||||||
p++;
|
p++;
|
||||||
tagLen--;
|
tagLen--;
|
||||||
}
|
}
|
||||||
|
11
src/update.c
11
src/update.c
@ -430,7 +430,7 @@ update_container_file( struct directory* directory,
|
|||||||
{
|
{
|
||||||
char* vtrack = NULL;
|
char* vtrack = NULL;
|
||||||
unsigned int tnum = 0;
|
unsigned int tnum = 0;
|
||||||
const char* pathname = map_directory_child_fs(directory, name);
|
char* pathname = map_directory_child_fs(directory, name);
|
||||||
struct directory* contdir = dirvec_find(&directory->children, name);
|
struct directory* contdir = dirvec_find(&directory->children, name);
|
||||||
|
|
||||||
// directory exists already
|
// directory exists already
|
||||||
@ -446,8 +446,10 @@ update_container_file( struct directory* directory,
|
|||||||
|
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
g_free(pathname);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contdir = make_subdir(directory, name);
|
contdir = make_subdir(directory, name);
|
||||||
@ -473,6 +475,8 @@ update_container_file( struct directory* directory,
|
|||||||
g_free(vtrack);
|
g_free(vtrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(pathname);
|
||||||
|
|
||||||
if (tnum == 1)
|
if (tnum == 1)
|
||||||
{
|
{
|
||||||
delete_directory(contdir);
|
delete_directory(contdir);
|
||||||
@ -767,7 +771,6 @@ static void * update_task(void *_path)
|
|||||||
{
|
{
|
||||||
if (_path != NULL && !isRootDirectory(_path)) {
|
if (_path != NULL && !isRootDirectory(_path)) {
|
||||||
updatePath((char *)_path);
|
updatePath((char *)_path);
|
||||||
g_free(_path);
|
|
||||||
} else {
|
} else {
|
||||||
struct directory *directory = db_get_root();
|
struct directory *directory = db_get_root();
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@ -776,6 +779,8 @@ static void * update_task(void *_path)
|
|||||||
updateDirectory(directory, &st);
|
updateDirectory(directory, &st);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(_path);
|
||||||
|
|
||||||
if (modified || !db_exists())
|
if (modified || !db_exists())
|
||||||
db_save();
|
db_save();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user