From 90fe4c5124e3fd335f05804d3cc47ba996e62b14 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 3 Jan 2013 10:16:05 +0100 Subject: TextFile: convert to a class --- src/DatabaseSave.cxx | 15 ++++--------- src/DatabaseSave.hxx | 3 ++- src/DirectorySave.cxx | 27 ++++++++++------------- src/DirectorySave.hxx | 4 ++-- src/PlaylistDatabase.cxx | 6 ++--- src/PlaylistDatabase.hxx | 5 +++-- src/PlaylistFile.cxx | 9 +++----- src/PlaylistState.cxx | 14 ++++++------ src/PlaylistState.hxx | 3 ++- src/QueueSave.cxx | 7 +++--- src/QueueSave.hxx | 4 ++-- src/SongSave.cxx | 6 ++--- src/SongSave.hxx | 5 +++-- src/StateFile.cxx | 16 ++++---------- src/TextFile.cxx | 11 ++------- src/TextFile.hxx | 49 ++++++++++++++++++++++++++++++++--------- src/db/SimpleDatabasePlugin.cxx | 11 ++++----- 17 files changed, 97 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/DatabaseSave.cxx b/src/DatabaseSave.cxx index 5bd50f55f..78a2c4939 100644 --- a/src/DatabaseSave.cxx +++ b/src/DatabaseSave.cxx @@ -77,9 +77,8 @@ db_save_internal(FILE *fp, const Directory *music_root) } bool -db_load_internal(FILE *fp, Directory *music_root, GError **error) +db_load_internal(TextFile &file, Directory *music_root, GError **error) { - GString *buffer = g_string_sized_new(1024); char *line; int format = 0; bool found_charset = false, found_version = false; @@ -89,16 +88,15 @@ db_load_internal(FILE *fp, Directory *music_root, GError **error) assert(music_root != NULL); /* get initial info */ - line = read_text_line(fp, buffer); + line = file.ReadLine(); if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) { g_set_error(error, db_quark(), 0, "Database corrupted"); - g_string_free(buffer, true); return false; } memset(tags, false, sizeof(tags)); - while ((line = read_text_line(fp, buffer)) != NULL && + while ((line = file.ReadLine()) != NULL && strcmp(line, DIRECTORY_INFO_END) != 0) { if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) { format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1); @@ -106,7 +104,6 @@ db_load_internal(FILE *fp, Directory *music_root, GError **error) if (found_version) { g_set_error(error, db_quark(), 0, "Duplicate version line"); - g_string_free(buffer, true); return false; } @@ -117,7 +114,6 @@ db_load_internal(FILE *fp, Directory *music_root, GError **error) if (found_charset) { g_set_error(error, db_quark(), 0, "Duplicate charset line"); - g_string_free(buffer, true); return false; } @@ -132,7 +128,6 @@ db_load_internal(FILE *fp, Directory *music_root, GError **error) "\"%s\" instead of \"%s\"; " "discarding database file", new_charset, old_charset); - g_string_free(buffer, true); return false; } } else if (g_str_has_prefix(line, DB_TAG_PREFIX)) { @@ -150,7 +145,6 @@ db_load_internal(FILE *fp, Directory *music_root, GError **error) } else { g_set_error(error, db_quark(), 0, "Malformed line: %s", line); - g_string_free(buffer, true); return false; } } @@ -174,9 +168,8 @@ db_load_internal(FILE *fp, Directory *music_root, GError **error) g_debug("reading DB"); db_lock(); - success = directory_load(fp, music_root, buffer, error); + success = directory_load(file, music_root, error); db_unlock(); - g_string_free(buffer, true); return success; } diff --git a/src/DatabaseSave.hxx b/src/DatabaseSave.hxx index 5133961fe..40048f261 100644 --- a/src/DatabaseSave.hxx +++ b/src/DatabaseSave.hxx @@ -25,11 +25,12 @@ #include struct Directory; +class TextFile; void db_save_internal(FILE *file, const Directory *root); bool -db_load_internal(FILE *file, Directory *root, GError **error); +db_load_internal(TextFile &file, Directory *root, GError **error); #endif diff --git a/src/DirectorySave.cxx b/src/DirectorySave.cxx index 9c5df685f..6a5efb058 100644 --- a/src/DirectorySave.cxx +++ b/src/DirectorySave.cxx @@ -76,10 +76,9 @@ directory_save(FILE *fp, const Directory *directory) } static Directory * -directory_load_subdir(FILE *fp, Directory *parent, const char *name, - GString *buffer, GError **error_r) +directory_load_subdir(TextFile &file, Directory *parent, const char *name, + GError **error_r) { - const char *line; bool success; if (parent->FindChild(name) != nullptr) { @@ -90,7 +89,7 @@ directory_load_subdir(FILE *fp, Directory *parent, const char *name, Directory *directory = parent->CreateChild(name); - line = read_text_line(fp, buffer); + const char *line = file.ReadLine(); if (line == NULL) { g_set_error(error_r, directory_quark(), 0, "Unexpected end of file"); @@ -103,7 +102,7 @@ directory_load_subdir(FILE *fp, Directory *parent, const char *name, g_ascii_strtoull(line + sizeof(DIRECTORY_MTIME) - 1, NULL, 10); - line = read_text_line(fp, buffer); + line = file.ReadLine(); if (line == NULL) { g_set_error(error_r, directory_quark(), 0, "Unexpected end of file"); @@ -119,7 +118,7 @@ directory_load_subdir(FILE *fp, Directory *parent, const char *name, return NULL; } - success = directory_load(fp, directory, buffer, error_r); + success = directory_load(file, directory, error_r); if (!success) { directory->Delete(); return NULL; @@ -129,18 +128,17 @@ directory_load_subdir(FILE *fp, Directory *parent, const char *name, } bool -directory_load(FILE *fp, Directory *directory, - GString *buffer, GError **error) +directory_load(TextFile &file, Directory *directory, GError **error) { const char *line; - while ((line = read_text_line(fp, buffer)) != NULL && + while ((line = file.ReadLine()) != NULL && !g_str_has_prefix(line, DIRECTORY_END)) { if (g_str_has_prefix(line, DIRECTORY_DIR)) { Directory *subdir = - directory_load_subdir(fp, directory, + directory_load_subdir(file, directory, line + sizeof(DIRECTORY_DIR) - 1, - buffer, error); + error); if (subdir == NULL) return false; } else if (g_str_has_prefix(line, SONG_BEGIN)) { @@ -153,8 +151,7 @@ directory_load(FILE *fp, Directory *directory, return false; } - song = song_load(fp, directory, name, - buffer, error); + song = song_load(file, directory, name, error); if (song == NULL) return false; @@ -165,8 +162,8 @@ directory_load(FILE *fp, Directory *directory, buffer */ char *name = g_strdup(line + sizeof(PLAYLIST_META_BEGIN) - 1); - if (!playlist_metadata_load(fp, directory->playlists, - name, buffer, error)) { + if (!playlist_metadata_load(file, directory->playlists, + name, error)) { g_free(name); return false; } diff --git a/src/DirectorySave.hxx b/src/DirectorySave.hxx index 83b820618..a7f3034a7 100644 --- a/src/DirectorySave.hxx +++ b/src/DirectorySave.hxx @@ -25,12 +25,12 @@ #include struct Directory; +class TextFile; void directory_save(FILE *fp, const Directory *directory); bool -directory_load(FILE *fp, Directory *directory, - GString *buffer, GError **error); +directory_load(TextFile &file, Directory *directory, GError **error); #endif diff --git a/src/PlaylistDatabase.cxx b/src/PlaylistDatabase.cxx index 984af4adc..edc6a2815 100644 --- a/src/PlaylistDatabase.cxx +++ b/src/PlaylistDatabase.cxx @@ -46,15 +46,15 @@ playlist_vector_save(FILE *fp, const PlaylistVector &pv) } bool -playlist_metadata_load(FILE *fp, PlaylistVector &pv, const char *name, - GString *buffer, GError **error_r) +playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name, + GError **error_r) { PlaylistInfo pm(name, 0); char *line, *colon; const char *value; - while ((line = read_text_line(fp, buffer)) != NULL && + while ((line = file.ReadLine()) != NULL && strcmp(line, "playlist_end") != 0) { colon = strchr(line, ':'); if (colon == NULL || colon == line) { diff --git a/src/PlaylistDatabase.hxx b/src/PlaylistDatabase.hxx index da2bb6b97..f5f039267 100644 --- a/src/PlaylistDatabase.hxx +++ b/src/PlaylistDatabase.hxx @@ -28,12 +28,13 @@ #define PLAYLIST_META_BEGIN "playlist_begin: " class PlaylistVector; +class TextFile; void playlist_vector_save(FILE *fp, const PlaylistVector &pv); bool -playlist_metadata_load(FILE *fp, PlaylistVector &pv, const char *name, - GString *buffer, GError **error_r); +playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name, + GError **error_r); #endif diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index e03cc5e54..67ffdff4f 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -236,16 +236,14 @@ LoadPlaylistFile(const char *utf8path, GError **error_r) if (path_fs == NULL) return contents; - FILE *file = fopen(path_fs, "r"); - g_free(path_fs); - if (file == NULL) { + TextFile file(path_fs); + if (file.HasFailed()) { playlist_errno(error_r); return contents; } - GString *buffer = g_string_sized_new(1024); char *s; - while ((s = read_text_line(file, buffer)) != NULL) { + while ((s = file.ReadLine()) != NULL) { if (*s == 0 || *s == PLAYLIST_COMMENT) continue; @@ -265,7 +263,6 @@ LoadPlaylistFile(const char *utf8path, GError **error_r) break; } - fclose(file); return contents; } diff --git a/src/PlaylistState.cxx b/src/PlaylistState.cxx index c45c2a834..1bb18250f 100644 --- a/src/PlaylistState.cxx +++ b/src/PlaylistState.cxx @@ -104,18 +104,18 @@ playlist_state_save(FILE *fp, const struct playlist *playlist, } static void -playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist) +playlist_state_load(TextFile &file, struct playlist *playlist) { - const char *line = read_text_line(fp, buffer); + const char *line = file.ReadLine(); if (line == NULL) { g_warning("No playlist in state file"); return; } while (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_PLAYLIST_END)) { - queue_load_song(fp, buffer, line, &playlist->queue); + queue_load_song(file, line, &playlist->queue); - line = read_text_line(fp, buffer); + line = file.ReadLine(); if (line == NULL) { g_warning("'" PLAYLIST_STATE_FILE_PLAYLIST_END "' not found in state file"); @@ -127,7 +127,7 @@ playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist) } bool -playlist_state_restore(const char *line, FILE *fp, GString *buffer, +playlist_state_restore(const char *line, TextFile &file, struct playlist *playlist, struct player_control *pc) { int current = -1; @@ -145,7 +145,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer, else if (strcmp(line, PLAYLIST_STATE_FILE_STATE_PAUSE) == 0) state = PLAYER_STATE_PAUSE; - while ((line = read_text_line(fp, buffer)) != NULL) { + while ((line = file.ReadLine()) != NULL) { if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_TIME)) { seek_time = atoi(&(line[strlen(PLAYLIST_STATE_FILE_TIME)])); @@ -189,7 +189,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer, (PLAYLIST_STATE_FILE_CURRENT)])); } else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)) { - playlist_state_load(fp, buffer, playlist); + playlist_state_load(file, playlist); } } diff --git a/src/PlaylistState.hxx b/src/PlaylistState.hxx index d38fe9d12..572f6fb4a 100644 --- a/src/PlaylistState.hxx +++ b/src/PlaylistState.hxx @@ -30,13 +30,14 @@ struct playlist; struct player_control; +class TextFile; void playlist_state_save(FILE *fp, const struct playlist *playlist, struct player_control *pc); bool -playlist_state_restore(const char *line, FILE *fp, GString *buffer, +playlist_state_restore(const char *line, TextFile &file, struct playlist *playlist, struct player_control *pc); /** diff --git a/src/QueueSave.cxx b/src/QueueSave.cxx index a468013f4..6ba48f336 100644 --- a/src/QueueSave.cxx +++ b/src/QueueSave.cxx @@ -71,8 +71,7 @@ queue_save(FILE *fp, const struct queue *queue) } void -queue_load_song(FILE *fp, GString *buffer, const char *line, - struct queue *queue) +queue_load_song(TextFile &file, const char *line, queue *queue) { if (queue_is_full(queue)) return; @@ -81,7 +80,7 @@ queue_load_song(FILE *fp, GString *buffer, const char *line, if (g_str_has_prefix(line, PRIO_LABEL)) { priority = strtoul(line + sizeof(PRIO_LABEL) - 1, NULL, 10); - line = read_text_line(fp, buffer); + line = file.ReadLine(); if (line == NULL) return; } @@ -95,7 +94,7 @@ queue_load_song(FILE *fp, GString *buffer, const char *line, return; GError *error = NULL; - song = song_load(fp, NULL, uri, buffer, &error); + song = song_load(file, NULL, uri, &error); if (song == NULL) { g_warning("%s", error->message); g_error_free(error); diff --git a/src/QueueSave.hxx b/src/QueueSave.hxx index d6b09329b..dc4a764a9 100644 --- a/src/QueueSave.hxx +++ b/src/QueueSave.hxx @@ -29,6 +29,7 @@ #include struct queue; +class TextFile; void queue_save(FILE *fp, const struct queue *queue); @@ -37,7 +38,6 @@ queue_save(FILE *fp, const struct queue *queue); * Loads one song from the state file and appends it to the queue. */ void -queue_load_song(FILE *fp, GString *buffer, const char *line, - struct queue *queue); +queue_load_song(TextFile &file, const char *line, queue *queue); #endif diff --git a/src/SongSave.cxx b/src/SongSave.cxx index cbc2536b1..a32b7fecc 100644 --- a/src/SongSave.cxx +++ b/src/SongSave.cxx @@ -63,8 +63,8 @@ song_save(FILE *fp, const struct song *song) } struct song * -song_load(FILE *fp, Directory *parent, const char *uri, - GString *buffer, GError **error_r) +song_load(TextFile &file, Directory *parent, const char *uri, + GError **error_r) { struct song *song = parent != NULL ? song_file_new(uri, parent) @@ -73,7 +73,7 @@ song_load(FILE *fp, Directory *parent, const char *uri, enum tag_type type; const char *value; - while ((line = read_text_line(fp, buffer)) != NULL && + while ((line = file.ReadLine()) != NULL && strcmp(line, SONG_END) != 0) { colon = strchr(line, ':'); if (colon == NULL || colon == line) { diff --git a/src/SongSave.hxx b/src/SongSave.hxx index 81838a3ee..3214c545f 100644 --- a/src/SongSave.hxx +++ b/src/SongSave.hxx @@ -28,6 +28,7 @@ struct song; struct Directory; +class TextFile; void song_save(FILE *fp, const struct song *song); @@ -41,7 +42,7 @@ song_save(FILE *fp, const struct song *song); * @return true on success, false on error */ struct song * -song_load(FILE *fp, Directory *parent, const char *uri, - GString *buffer, GError **error_r); +song_load(TextFile &file, Directory *parent, const char *uri, + GError **error_r); #endif diff --git a/src/StateFile.cxx b/src/StateFile.cxx index 5af60ddba..cd344eb0e 100644 --- a/src/StateFile.cxx +++ b/src/StateFile.cxx @@ -78,39 +78,31 @@ state_file_write(struct player_control *pc) static void state_file_read(struct player_control *pc) { - FILE *fp; bool success; assert(state_file_path != NULL); g_debug("Loading state file %s", state_file_path); - fp = fopen(state_file_path, "r"); - if (G_UNLIKELY(!fp)) { + TextFile file(state_file_path); + if (file.HasFailed()) { g_warning("failed to open %s: %s", state_file_path, g_strerror(errno)); return; } - GString *buffer = g_string_sized_new(1024); const char *line; - while ((line = read_text_line(fp, buffer)) != NULL) { + while ((line = file.ReadLine()) != NULL) { success = read_sw_volume_state(line) || audio_output_state_read(line) || - playlist_state_restore(line, fp, buffer, - &g_playlist, pc); + playlist_state_restore(line, file, &g_playlist, pc); if (!success) g_warning("Unrecognized line in state file: %s", line); } - fclose(fp); - prev_volume_version = sw_volume_state_get_hash(); prev_output_version = audio_output_state_get_version(); prev_playlist_version = playlist_state_get_hash(&g_playlist, pc); - - - g_string_free(buffer, true); } /** diff --git a/src/TextFile.cxx b/src/TextFile.cxx index ec9c5d442..4ad59ee4a 100644 --- a/src/TextFile.cxx +++ b/src/TextFile.cxx @@ -24,21 +24,14 @@ #include char * -read_text_line(FILE *file, GString *buffer) +TextFile::ReadLine() { - 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); + assert(buffer->allocated_len >= step); while (buffer->len < max_length) { p = fgets(buffer->str + length, diff --git a/src/TextFile.hxx b/src/TextFile.hxx index 5bd6dbd3c..25f6ea7a8 100644 --- a/src/TextFile.hxx +++ b/src/TextFile.hxx @@ -20,20 +20,47 @@ #ifndef MPD_TEXT_FILE_HXX #define MPD_TEXT_FILE_HXX +#include "gcc.h" + #include #include -/** - * 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); +class TextFile { + static constexpr size_t max_length = 512 * 1024; + static constexpr size_t step = 1024; + + FILE *const file; + + GString *const buffer; + +public: + TextFile(const char *path_fs) + :file(fopen(path_fs, "r")), buffer(g_string_sized_new(step)) {} + + TextFile(const TextFile &other) = delete; + + ~TextFile() { + if (file != nullptr) + fclose(file); + + g_string_free(buffer, true); + } + + bool HasFailed() const { + return gcc_unlikely(file == nullptr); + } + + /** + * 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 *ReadLine(); +}; #endif diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx index 04e319498..84e4e7cee 100644 --- a/src/db/SimpleDatabasePlugin.cxx +++ b/src/db/SimpleDatabasePlugin.cxx @@ -26,6 +26,7 @@ #include "DatabaseSave.hxx" #include "DatabaseLock.hxx" #include "db_error.h" +#include "TextFile.hxx" extern "C" { #include "conf.h" @@ -155,20 +156,16 @@ SimpleDatabase::Load(GError **error_r) assert(!path.empty()); assert(root != NULL); - FILE *fp = fopen(path.c_str(), "r"); - if (fp == NULL) { + TextFile file(path.c_str()); + if (file.HasFailed()) { g_set_error(error_r, simple_db_quark(), errno, "Failed to open database file \"%s\": %s", path.c_str(), g_strerror(errno)); return false; } - if (!db_load_internal(fp, root, error_r)) { - fclose(fp); + if (!db_load_internal(file, root, error_r)) return false; - } - - fclose(fp); struct stat st; if (stat(path.c_str(), &st) == 0) -- cgit v1.2.3