diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/database.c | 38 | ||||
-rw-r--r-- | src/directory_save.c | 45 | ||||
-rw-r--r-- | src/directory_save.h | 3 | ||||
-rw-r--r-- | src/song_save.c | 31 | ||||
-rw-r--r-- | src/song_save.h | 2 | ||||
-rw-r--r-- | src/text_file.c | 62 | ||||
-rw-r--r-- | src/text_file.h | 39 |
9 files changed, 164 insertions, 59 deletions
diff --git a/Makefile.am b/Makefile.am index 4cbe6774d..d7167a4cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,6 +88,7 @@ mpd_headers = \ src/input/file_input_plugin.h \ src/input/curl_input_plugin.h \ src/input/mms_input_plugin.h \ + src/text_file.h \ src/text_input_stream.h \ src/icy_server.h \ src/icy_metadata.h \ @@ -285,6 +286,7 @@ src_mpd_SOURCES = \ src/tag_print.c \ src/tag_save.c \ src/tokenizer.c \ + src/text_file.c \ src/text_input_stream.c \ src/strset.c \ src/uri.c \ @@ -53,6 +53,7 @@ ver 0.16 (20??/??/??) * renamed option "--stdout" to "--stderr" * removed options --create-db and --no-create-db * state_file: save only if something has changed +* database: eliminated maximum line length * obey $(sysconfdir) for default mpd.conf location * build with large file support by default * require GLib 2.16 diff --git a/src/database.c b/src/database.c index 5a06dda98..dc0ef3686 100644 --- a/src/database.c +++ b/src/database.c @@ -23,6 +23,7 @@ #include "song.h" #include "path.h" #include "stats.h" +#include "text_file.h" #include "config.h" #include <glib.h> @@ -256,7 +257,8 @@ db_load(GError **error) { FILE *fp = NULL; struct stat st; - char buffer[100]; + GString *buffer = g_string_sized_new(1024); + char *line; bool found_charset = false, found_version = false; bool success; @@ -270,50 +272,45 @@ db_load(GError **error) g_set_error(error, db_quark(), errno, "Failed to open database file \"%s\": %s", database_path, strerror(errno)); + g_string_free(buffer, true); return false; } /* get initial info */ - if (!fgets(buffer, sizeof(buffer), fp)) { - fclose(fp); - g_set_error(error, db_quark(), 0, "Unexpected end of file"); - return false; - } - - g_strchomp(buffer); - - if (0 != strcmp(DIRECTORY_INFO_BEGIN, buffer)) { + line = read_text_line(fp, buffer); + if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) { fclose(fp); g_set_error(error, db_quark(), 0, "Database corrupted"); + g_string_free(buffer, true); return false; } - while (fgets(buffer, sizeof(buffer), fp) && - !g_str_has_prefix(buffer, DIRECTORY_INFO_END)) { - g_strchomp(buffer); - - if (g_str_has_prefix(buffer, DIRECTORY_MPD_VERSION)) { + while ((line = read_text_line(fp, buffer)) != NULL && + !g_str_has_prefix(line, DIRECTORY_INFO_END)) { + if (g_str_has_prefix(line, DIRECTORY_MPD_VERSION)) { if (found_version) { fclose(fp); g_set_error(error, db_quark(), 0, "Duplicate version line"); + g_string_free(buffer, true); return false; } found_version = true; - } else if (g_str_has_prefix(buffer, DIRECTORY_FS_CHARSET)) { + } else if (g_str_has_prefix(line, DIRECTORY_FS_CHARSET)) { const char *new_charset, *old_charset; if (found_charset) { fclose(fp); g_set_error(error, db_quark(), 0, "Duplicate charset line"); + g_string_free(buffer, true); return false; } found_charset = true; - new_charset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]); + new_charset = line + sizeof(DIRECTORY_FS_CHARSET) - 1; old_charset = path_get_fs_charset(); if (old_charset != NULL && strcmp(new_charset, old_charset)) { @@ -323,19 +320,22 @@ db_load(GError **error) "\"%s\" instead of \"%s\"; " "discarding database file", new_charset, old_charset); + g_string_free(buffer, true); return false; } } else { fclose(fp); g_set_error(error, db_quark(), 0, - "Malformed line: %s", buffer); + "Malformed line: %s", line); + g_string_free(buffer, true); return false; } } g_debug("reading DB"); - success = directory_load(fp, music_root, error); + success = directory_load(fp, music_root, buffer, error); + g_string_free(buffer, true); while (fclose(fp) && errno == EINTR) ; if (!success) diff --git a/src/directory_save.c b/src/directory_save.c index f353c7c2e..ed4f0190f 100644 --- a/src/directory_save.c +++ b/src/directory_save.c @@ -20,7 +20,7 @@ #include "directory_save.h" #include "directory.h" #include "song.h" -#include "path.h" +#include "text_file.h" #include "song_save.h" #include <assert.h> @@ -80,10 +80,10 @@ directory_save(FILE *fp, struct directory *directory) static struct directory * directory_load_subdir(FILE *fp, struct directory *parent, const char *name, - GError **error_r) + GString *buffer, GError **error_r) { - char buffer[MPD_PATH_MAX * 2]; struct directory *directory; + const char *line; bool success; if (directory_get_child(parent, name) != NULL) { @@ -101,19 +101,21 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name, g_free(path); } - if (!fgets(buffer, sizeof(buffer), fp)) { + line = read_text_line(fp, buffer); + if (line == NULL) { g_set_error(error_r, directory_quark(), 0, "Unexpected end of file"); directory_free(directory); return NULL; } - if (g_str_has_prefix(buffer, DIRECTORY_MTIME)) { + if (g_str_has_prefix(line, DIRECTORY_MTIME)) { directory->mtime = - g_ascii_strtoull(buffer + sizeof(DIRECTORY_MTIME) - 1, + g_ascii_strtoull(line + sizeof(DIRECTORY_MTIME) - 1, NULL, 10); - if (!fgets(buffer, sizeof(buffer), fp)) { + line = read_text_line(fp, buffer); + if (line == NULL) { g_set_error(error_r, directory_quark(), 0, "Unexpected end of file"); directory_free(directory); @@ -121,14 +123,14 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name, } } - if (!g_str_has_prefix(buffer, DIRECTORY_BEGIN)) { + if (!g_str_has_prefix(line, DIRECTORY_BEGIN)) { g_set_error(error_r, directory_quark(), 0, - "Malformed line: %s", buffer); + "Malformed line: %s", line); directory_free(directory); return NULL; } - success = directory_load(fp, directory, error_r); + success = directory_load(fp, directory, buffer, error_r); if (!success) { directory_free(directory); return NULL; @@ -138,32 +140,31 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name, } bool -directory_load(FILE *fp, struct directory *directory, GError **error) +directory_load(FILE *fp, struct directory *directory, + GString *buffer, GError **error) { - char buffer[MPD_PATH_MAX * 2]; + const char *line; bool success; - while (fgets(buffer, sizeof(buffer), fp) - && !g_str_has_prefix(buffer, DIRECTORY_END)) { - g_strchomp(buffer); - - if (g_str_has_prefix(buffer, DIRECTORY_DIR)) { + while ((line = read_text_line(fp, buffer)) != NULL && + !g_str_has_prefix(line, DIRECTORY_END)) { + if (g_str_has_prefix(line, DIRECTORY_DIR)) { struct directory *subdir = directory_load_subdir(fp, directory, - buffer + sizeof(DIRECTORY_DIR) - 1, - error); + line + sizeof(DIRECTORY_DIR) - 1, + buffer, error); if (subdir == NULL) return false; dirvec_add(&directory->children, subdir); - } else if (g_str_has_prefix(buffer, SONG_BEGIN)) { + } else if (g_str_has_prefix(line, SONG_BEGIN)) { success = songvec_load(fp, &directory->songs, - directory, error); + directory, buffer, error); if (!success) return false; } else { g_set_error(error, directory_quark(), 0, - "Malformed line: %s", buffer); + "Malformed line: %s", line); return false; } } diff --git a/src/directory_save.h b/src/directory_save.h index 28ec094ad..d9daa3fed 100644 --- a/src/directory_save.h +++ b/src/directory_save.h @@ -31,6 +31,7 @@ int directory_save(FILE *fp, struct directory *directory); bool -directory_load(FILE *fp, struct directory *directory, GError **error); +directory_load(FILE *fp, struct directory *directory, + GString *buffer, GError **error); #endif diff --git a/src/song_save.c b/src/song_save.c index b18057201..4b8409d86 100644 --- a/src/song_save.c +++ b/src/song_save.c @@ -21,8 +21,8 @@ #include "song.h" #include "tag_save.h" #include "directory.h" -#include "path.h" #include "tag.h" +#include "text_file.h" #include <glib.h> @@ -117,32 +117,31 @@ parse_tag_value(char *buffer, enum tag_type *type_r) bool songvec_load(FILE *fp, struct songvec *sv, struct directory *parent, - GError **error_r) + GString *buffer, GError **error_r) { - char buffer[MPD_PATH_MAX + 1024]; + char *line; struct song *song = NULL; enum tag_type type; const char *value; - while (fgets(buffer, sizeof(buffer), fp) && - !g_str_has_prefix(buffer, SONG_END)) { - g_strchomp(buffer); + while ((line = read_text_line(fp, buffer)) != NULL && + !g_str_has_prefix(line, SONG_END)) { - if (0 == strncmp(SONG_KEY, buffer, strlen(SONG_KEY))) { + if (0 == strncmp(SONG_KEY, line, strlen(SONG_KEY))) { if (song) commit_song(sv, song); - song = song_file_new(buffer + strlen(SONG_KEY), + song = song_file_new(line + strlen(SONG_KEY), parent); - } else if (*buffer == 0) { + } else if (*line == 0) { /* ignore empty lines (starting with '\0') */ } else if (song == NULL) { g_set_error(error_r, song_save_quark(), 0, "Problems reading song info"); return false; - } else if (0 == strncmp(SONG_FILE, buffer, strlen(SONG_FILE))) { + } else if (0 == strncmp(SONG_FILE, line, strlen(SONG_FILE))) { /* we don't need this info anymore */ - } else if ((value = parse_tag_value(buffer, + } else if ((value = parse_tag_value(line, &type)) != NULL) { if (!song->tag) { song->tag = tag_new(); @@ -150,18 +149,18 @@ songvec_load(FILE *fp, struct songvec *sv, struct directory *parent, } tag_add_item(song->tag, type, value); - } else if (0 == strncmp(SONG_TIME, buffer, strlen(SONG_TIME))) { + } else if (0 == strncmp(SONG_TIME, line, strlen(SONG_TIME))) { if (!song->tag) { song->tag = tag_new(); tag_begin_add(song->tag); } - song->tag->time = atoi(&(buffer[strlen(SONG_TIME)])); - } else if (0 == strncmp(SONG_MTIME, buffer, strlen(SONG_MTIME))) { - song->mtime = atoi(&(buffer[strlen(SONG_MTIME)])); + song->tag->time = atoi(&(line[strlen(SONG_TIME)])); + } else if (0 == strncmp(SONG_MTIME, line, strlen(SONG_MTIME))) { + song->mtime = atoi(&(line[strlen(SONG_MTIME)])); } else { g_set_error(error_r, song_save_quark(), 0, - "unknown line in db: %s", buffer); + "unknown line in db: %s", line); return false; } } diff --git a/src/song_save.h b/src/song_save.h index 36e03584e..cd62c986d 100644 --- a/src/song_save.h +++ b/src/song_save.h @@ -40,6 +40,6 @@ void songvec_save(FILE *fp, struct songvec *sv); */ bool songvec_load(FILE *file, struct songvec *sv, struct directory *parent, - GError **error_r); + GString *buffer, GError **error_r); #endif diff --git a/src/text_file.c b/src/text_file.c new file mode 100644 index 000000000..16698fc57 --- /dev/null +++ b/src/text_file.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "text_file.h" + +#include <assert.h> +#include <string.h> + +char * +read_text_line(FILE *file, GString *buffer) +{ + enum { + max_length = 512 * 1024, + step = 1024, + }; + + gsize length = 0, i; + char *p; + + assert(file != NULL); + assert(buffer != NULL); + + if (buffer->allocated_len < step) + g_string_set_size(buffer, step); + + while (buffer->len < max_length) { + p = fgets(buffer->str + length, + buffer->allocated_len - length, file); + if (p == NULL) { + if (length == 0 || ferror(file)) + return NULL; + break; + } + + i = strlen(buffer->str + length); + length += i; + if (i < step - 1 || buffer->str[length - 1] == '\n') + break; + + g_string_set_size(buffer, length + step); + } + + g_string_set_size(buffer, length); + g_strchomp(buffer->str); + return buffer->str; +} diff --git a/src/text_file.h b/src/text_file.h new file mode 100644 index 000000000..bc5c92870 --- /dev/null +++ b/src/text_file.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TEXT_FILE_H +#define MPD_TEXT_FILE_H + +#include <glib.h> + +#include <stdio.h> + +/** + * Reads a line from the input file, and strips trailing space. There + * is a reasonable maximum line length, only to prevent denial of + * service. + * + * @param file the source file, opened in text mode + * @param buffer an allocator for the buffer + * @return a pointer to the line, or NULL on end-of-file or error + */ +char * +read_text_line(FILE *file, GString *buffer); + +#endif |