file: Open and close files
- Support OPEN_READ and OPEN_WRITE - Update the design to accomidate for error checking - Add lib/test.cpp containing basic functions that can be used for testing Signed-off-by: Bryan Schumaker <bjschuma@gmail.com>
This commit is contained in:
parent
e0a32c10ac
commit
ecd56831f6
20
design.txt
20
design.txt
|
@ -161,24 +161,27 @@ On-disk files: (lib/file.cpp)
|
|||
enum OpenMode {
|
||||
OPEN_READ,
|
||||
OPEN_WRITE,
|
||||
NOT_OPEN,
|
||||
}
|
||||
|
||||
- File:
|
||||
class File {
|
||||
private:
|
||||
union {
|
||||
ifstream in;
|
||||
ofstream out;
|
||||
} f;
|
||||
ifstream in;
|
||||
ofstream out;
|
||||
|
||||
unsigned int version;
|
||||
OpenMode mode;
|
||||
FileLocHint hint;
|
||||
string filepath;
|
||||
|
||||
public:
|
||||
File(string, FileLocHint);
|
||||
~File();
|
||||
const char *get_filepath();
|
||||
bool exists();
|
||||
bool open(OpenMode);
|
||||
bool close();
|
||||
|
||||
const File &operator<<(File &);
|
||||
const File &operator>>(File &);
|
||||
|
@ -198,6 +201,9 @@ On-disk files: (lib/file.cpp)
|
|||
$HOME/.ocarina/
|
||||
$HOME/.ocarina-debug/
|
||||
|
||||
File : ~File()
|
||||
Close the file stream if it is open.
|
||||
|
||||
const char *get_filepath()
|
||||
Return the full filepath to the file.
|
||||
|
||||
|
@ -218,7 +224,11 @@ On-disk files: (lib/file.cpp)
|
|||
- Write version information to the start of the file
|
||||
- Return true
|
||||
|
||||
Return false if there are any errors opening the file.
|
||||
Return false if the file is already open.
|
||||
Return false if there are any other errors.
|
||||
|
||||
void File : close()
|
||||
Close a file after IO.
|
||||
|
||||
File : operator<<()
|
||||
Write data to the file.
|
||||
|
|
|
@ -42,24 +42,27 @@ On-disk files: (lib/file.cpp)
|
|||
enum OpenMode {
|
||||
OPEN_READ,
|
||||
OPEN_WRITE,
|
||||
NOT_OPEN,
|
||||
}
|
||||
|
||||
- File:
|
||||
class File {
|
||||
private:
|
||||
union {
|
||||
ifstream in;
|
||||
ofstream out;
|
||||
} f;
|
||||
ifstream in;
|
||||
ofstream out;
|
||||
|
||||
unsigned int version;
|
||||
OpenMode mode;
|
||||
FileLocHint hint;
|
||||
string filepath;
|
||||
|
||||
public:
|
||||
File(string, FileLocHint);
|
||||
~File();
|
||||
const char *get_filepath();
|
||||
bool exists();
|
||||
bool open(OpenMode);
|
||||
bool close();
|
||||
|
||||
const File &operator<<(File &);
|
||||
const File &operator>>(File &);
|
||||
|
@ -79,6 +82,9 @@ On-disk files: (lib/file.cpp)
|
|||
$HOME/.ocarina/
|
||||
$HOME/.ocarina-debug/
|
||||
|
||||
File : ~File()
|
||||
Close the file stream if it is open.
|
||||
|
||||
const char *get_filepath()
|
||||
Return the full filepath to the file.
|
||||
|
||||
|
@ -99,7 +105,11 @@ On-disk files: (lib/file.cpp)
|
|||
- Write version information to the start of the file
|
||||
- Return true
|
||||
|
||||
Return false if there are any errors opening the file.
|
||||
Return false if the file is already open.
|
||||
Return false if there are any other errors.
|
||||
|
||||
void File : close()
|
||||
Close a file after IO.
|
||||
|
||||
File : operator<<()
|
||||
Write data to the file.
|
||||
|
|
|
@ -4,22 +4,42 @@
|
|||
#ifndef OCARINA_FILE_H
|
||||
#define OCARINA_FILE_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#define FILE_VERSION 0
|
||||
|
||||
enum FileLocHint {
|
||||
FILE_TYPE_CONFIG,
|
||||
FILE_TYPE_DATA,
|
||||
FILE_TYPE_LEGACY,
|
||||
};
|
||||
|
||||
enum OpenMode {
|
||||
OPEN_READ,
|
||||
OPEN_WRITE,
|
||||
NOT_OPEN,
|
||||
};
|
||||
|
||||
class File {
|
||||
private:
|
||||
std::fstream stream;
|
||||
OpenMode mode;
|
||||
FileLocHint hint;
|
||||
std::string filepath;
|
||||
unsigned int version;
|
||||
|
||||
void find_dir(std::string &);
|
||||
bool open_read();
|
||||
bool open_write();
|
||||
|
||||
public:
|
||||
File(std::string, FileLocHint);
|
||||
~File();
|
||||
const char *get_filepath();
|
||||
bool exists();
|
||||
bool open(OpenMode);
|
||||
bool close();
|
||||
};
|
||||
|
||||
#endif /* OCARINA_FILE_H */
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef OCARINA_TEST_H
|
||||
#define OCARINA_TEST_H
|
||||
#ifdef CONFIG_TEST
|
||||
|
||||
void rm_test_config();
|
||||
void rm_test_data();
|
||||
|
||||
#endif /* CONFIG_TEST */
|
||||
#endif /* OCARINA_TEST_H */
|
|
@ -7,4 +7,7 @@ if CONFIG.FILE:
|
|||
CONFIG.package("glib-2.0")
|
||||
build += [ env.Object("file.cpp") ]
|
||||
|
||||
if CONFIG.TEST:
|
||||
build += [ env.Object("test.cpp") ]
|
||||
|
||||
Return("build")
|
||||
|
|
128
lib/file.cpp
128
lib/file.cpp
|
@ -2,6 +2,7 @@
|
|||
* Copyright 2013 (c) Bryan Schumaker.
|
||||
*/
|
||||
#include <file.h>
|
||||
#include <print.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
@ -13,37 +14,118 @@
|
|||
#define OCARINA_DIR "ocarina"
|
||||
#endif
|
||||
|
||||
void File :: find_dir(std::string &res)
|
||||
{
|
||||
switch (hint) {
|
||||
case FILE_TYPE_CONFIG:
|
||||
res = g_get_user_config_dir();
|
||||
break;
|
||||
case FILE_TYPE_DATA:
|
||||
res = g_get_user_data_dir();
|
||||
break;
|
||||
case FILE_TYPE_LEGACY:
|
||||
res = g_get_home_dir();
|
||||
}
|
||||
|
||||
res += "/";
|
||||
switch (hint) {
|
||||
case FILE_TYPE_CONFIG:
|
||||
case FILE_TYPE_DATA:
|
||||
res += OCARINA_DIR;
|
||||
break;
|
||||
case FILE_TYPE_LEGACY:
|
||||
res += ".";
|
||||
res += OCARINA_DIR;
|
||||
}
|
||||
}
|
||||
|
||||
File :: File(std::string path, FileLocHint file_hint)
|
||||
: hint(file_hint)
|
||||
: mode(NOT_OPEN), hint(file_hint), version(FILE_VERSION)
|
||||
{
|
||||
std::string dir;
|
||||
|
||||
switch (file_hint) {
|
||||
case FILE_TYPE_CONFIG:
|
||||
dir = g_get_user_config_dir();
|
||||
break;
|
||||
case FILE_TYPE_DATA:
|
||||
dir = g_get_user_data_dir();
|
||||
break;
|
||||
case FILE_TYPE_LEGACY:
|
||||
dir = g_get_home_dir();
|
||||
}
|
||||
|
||||
dir += "/";
|
||||
switch (file_hint) {
|
||||
case FILE_TYPE_CONFIG:
|
||||
case FILE_TYPE_DATA:
|
||||
dir += OCARINA_DIR;
|
||||
break;
|
||||
case FILE_TYPE_LEGACY:
|
||||
dir += ".";
|
||||
dir += OCARINA_DIR;
|
||||
}
|
||||
|
||||
find_dir(dir);
|
||||
filepath = dir + "/" + path;
|
||||
}
|
||||
|
||||
File :: ~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
const char *File :: get_filepath()
|
||||
{
|
||||
return filepath.c_str();
|
||||
}
|
||||
|
||||
bool File :: exists()
|
||||
{
|
||||
return g_file_test(filepath.c_str(), G_FILE_TEST_EXISTS);
|
||||
}
|
||||
|
||||
bool File :: open_read()
|
||||
{
|
||||
if (!exists()) {
|
||||
dprint("ERROR: File does not exist (%s)\n", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.open(filepath.c_str(), std::fstream::in);
|
||||
if (stream.fail()) {
|
||||
dprint("ERROR: Could not open file for reading (%s)\n", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
mode = OPEN_READ;
|
||||
stream >> version;
|
||||
return stream.good();
|
||||
}
|
||||
|
||||
bool File :: open_write()
|
||||
{
|
||||
std::string dir;
|
||||
if (hint == FILE_TYPE_LEGACY) {
|
||||
dprint("ERROR: Cannot write to legacy files (%s)\n", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
find_dir(dir);
|
||||
if (g_mkdir_with_parents(dir.c_str(), 0755) != 0) {
|
||||
dprint("ERROR: Could not make directory (%s)\n", dir.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.open(filepath.c_str(), std::fstream::out);
|
||||
if (stream.fail()) {
|
||||
dprint("ERROR: Could not open file for writing (%s)\n", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
mode = OPEN_WRITE;
|
||||
stream << version << std::endl;
|
||||
return stream.good();
|
||||
}
|
||||
|
||||
bool File :: open(OpenMode m)
|
||||
{
|
||||
if (mode != NOT_OPEN) {
|
||||
dprint("ERROR: File is already open (%s)\n", filepath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m == NOT_OPEN) {
|
||||
dprint("ERROR: NOT_OPEN is not a legal OpenMode (%s)\n", filepath.c_str());
|
||||
return false;
|
||||
} else if (m == OPEN_READ)
|
||||
return open_read();
|
||||
else /* m == OPEN_WRITE */
|
||||
return open_write();
|
||||
}
|
||||
|
||||
bool File :: close()
|
||||
{
|
||||
if (mode != NOT_OPEN)
|
||||
stream.close();
|
||||
mode = NOT_OPEN;
|
||||
return stream.good();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Bryan Schumaker.
|
||||
*/
|
||||
#include <test.h>
|
||||
|
||||
#include <string>
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* A bit hackish, but it works... */
|
||||
static void rm_dir(const char *dir)
|
||||
{
|
||||
std::string cmd = "rm -frv ";
|
||||
cmd += dir;
|
||||
cmd += "/ocarina-test";
|
||||
system(cmd.c_str());
|
||||
}
|
||||
|
||||
void rm_test_config()
|
||||
{
|
||||
rm_dir(g_get_user_config_dir());
|
||||
}
|
||||
|
||||
void rm_test_data()
|
||||
{
|
||||
rm_dir(g_get_user_data_dir());
|
||||
}
|
|
@ -4,3 +4,4 @@ Import("Test", "CONFIG")
|
|||
CONFIG.FILE = True
|
||||
|
||||
Test("file", "compile.cpp")
|
||||
Test("file", "open.cpp")
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
removed ‘/home/anna/.local/share/ocarina-test/test.4’
|
||||
removed ‘/home/anna/.local/share/ocarina-test/test.3’
|
||||
removed directory: ‘/home/anna/.local/share/ocarina-test’
|
||||
|
||||
ERROR: NOT_OPEN is not a legal OpenMode (/home/anna/.local/share/ocarina-test/test.0)
|
||||
Test 0: Passed
|
||||
|
||||
ERROR: File does not exist (/home/anna/.local/share/ocarina-test/test.1)
|
||||
Test 1: Passed
|
||||
|
||||
ERROR: Cannot write to legacy files (/home/anna/.ocarina-test/test.2)
|
||||
Test 2: Passed
|
||||
|
||||
Test 3a: Passed
|
||||
ERROR: File is already open (/home/anna/.local/share/ocarina-test/test.3)
|
||||
Test 3b: Passed
|
||||
|
||||
Test 4a: Passed
|
||||
Test 4b: Passed
|
||||
Test 4c: Passed
|
||||
|
||||
Test 5: Passed
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Test opening files under various conditions
|
||||
*/
|
||||
#include <file.h>
|
||||
#include <test.h>
|
||||
#include <print.h>
|
||||
|
||||
static int test_result(const char *num, bool res, bool expected)
|
||||
{
|
||||
print("Test %s: ", num);
|
||||
if (res == expected) {
|
||||
print("Passed\n");
|
||||
return 0;
|
||||
} else {
|
||||
print("Failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int test_error(const char *num, FileLocHint hint, OpenMode mode)
|
||||
{
|
||||
std::string f = "test.";
|
||||
f += num;
|
||||
|
||||
File file(f.c_str(), hint);
|
||||
print("\n");
|
||||
return test_result(num, file.open(mode), false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to open a file with mode == NOT_OPEN
|
||||
*/
|
||||
int test_0()
|
||||
{
|
||||
return test_error("0", FILE_TYPE_DATA, NOT_OPEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to open a file that doesn't exist for reading
|
||||
*/
|
||||
int test_1()
|
||||
{
|
||||
return test_error("1", FILE_TYPE_DATA, OPEN_READ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to open a legacy file for writing
|
||||
*/
|
||||
int test_2()
|
||||
{
|
||||
return test_error("2", FILE_TYPE_LEGACY, OPEN_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to a file, then open it for reading WITHOUT closing first
|
||||
*/
|
||||
int test_3()
|
||||
{
|
||||
File file("test.3", FILE_TYPE_DATA);
|
||||
|
||||
printf("\n");
|
||||
if (test_result("3a", file.open(OPEN_WRITE), true) != 0)
|
||||
return 1;
|
||||
return test_result("3b", file.open(OPEN_READ), false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to a file, close it, then open it again for reading
|
||||
*/
|
||||
int test_4()
|
||||
{
|
||||
File file("test.4", FILE_TYPE_DATA);
|
||||
|
||||
printf("\n");
|
||||
if (test_result("4a", file.open(OPEN_WRITE), true) != 0)
|
||||
return 1;
|
||||
if (test_result("4b", file.close(), true) != 0)
|
||||
return 1;
|
||||
return test_result("4c", file.open(OPEN_READ), true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to close a file that was never opened
|
||||
*/
|
||||
int test_5()
|
||||
{
|
||||
File file("test.5", FILE_TYPE_DATA);
|
||||
|
||||
print("\n");
|
||||
return test_result("5", file.close(), true);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int failed = 0;
|
||||
rm_test_data();
|
||||
|
||||
failed += test_0();
|
||||
failed += test_1();
|
||||
failed += test_2();
|
||||
failed += test_3();
|
||||
failed += test_4();
|
||||
failed += test_5();
|
||||
|
||||
return failed;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
removed ‘/home/anna/.local/share/ocarina-test/test.4’
|
||||
removed ‘/home/anna/.local/share/ocarina-test/test.3’
|
||||
removed directory: ‘/home/anna/.local/share/ocarina-test’
|
||||
|
||||
Test 0: Passed
|
||||
|
||||
Test 1: Passed
|
||||
|
||||
Test 2: Passed
|
||||
|
||||
Test 3a: Passed
|
||||
Test 3b: Passed
|
||||
|
||||
Test 4a: Passed
|
||||
Test 4b: Passed
|
||||
Test 4c: Passed
|
||||
|
||||
Test 5: Passed
|
Loading…
Reference in New Issue