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
16
design.txt
16
design.txt
|
@ -161,24 +161,27 @@ On-disk files: (lib/file.cpp)
|
||||||
enum OpenMode {
|
enum OpenMode {
|
||||||
OPEN_READ,
|
OPEN_READ,
|
||||||
OPEN_WRITE,
|
OPEN_WRITE,
|
||||||
|
NOT_OPEN,
|
||||||
}
|
}
|
||||||
|
|
||||||
- File:
|
- File:
|
||||||
class File {
|
class File {
|
||||||
private:
|
private:
|
||||||
union {
|
|
||||||
ifstream in;
|
ifstream in;
|
||||||
ofstream out;
|
ofstream out;
|
||||||
} f;
|
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
OpenMode mode;
|
OpenMode mode;
|
||||||
FileLocHint hint;
|
FileLocHint hint;
|
||||||
string filepath;
|
string filepath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File(string, FileLocHint);
|
File(string, FileLocHint);
|
||||||
|
~File();
|
||||||
const char *get_filepath();
|
const char *get_filepath();
|
||||||
bool exists();
|
bool exists();
|
||||||
bool open(OpenMode);
|
bool open(OpenMode);
|
||||||
|
bool close();
|
||||||
|
|
||||||
const File &operator<<(File &);
|
const File &operator<<(File &);
|
||||||
const File &operator>>(File &);
|
const File &operator>>(File &);
|
||||||
|
@ -198,6 +201,9 @@ On-disk files: (lib/file.cpp)
|
||||||
$HOME/.ocarina/
|
$HOME/.ocarina/
|
||||||
$HOME/.ocarina-debug/
|
$HOME/.ocarina-debug/
|
||||||
|
|
||||||
|
File : ~File()
|
||||||
|
Close the file stream if it is open.
|
||||||
|
|
||||||
const char *get_filepath()
|
const char *get_filepath()
|
||||||
Return the full filepath to the file.
|
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
|
- Write version information to the start of the file
|
||||||
- Return true
|
- 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<<()
|
File : operator<<()
|
||||||
Write data to the file.
|
Write data to the file.
|
||||||
|
|
|
@ -42,24 +42,27 @@ On-disk files: (lib/file.cpp)
|
||||||
enum OpenMode {
|
enum OpenMode {
|
||||||
OPEN_READ,
|
OPEN_READ,
|
||||||
OPEN_WRITE,
|
OPEN_WRITE,
|
||||||
|
NOT_OPEN,
|
||||||
}
|
}
|
||||||
|
|
||||||
- File:
|
- File:
|
||||||
class File {
|
class File {
|
||||||
private:
|
private:
|
||||||
union {
|
|
||||||
ifstream in;
|
ifstream in;
|
||||||
ofstream out;
|
ofstream out;
|
||||||
} f;
|
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
OpenMode mode;
|
OpenMode mode;
|
||||||
FileLocHint hint;
|
FileLocHint hint;
|
||||||
string filepath;
|
string filepath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File(string, FileLocHint);
|
File(string, FileLocHint);
|
||||||
|
~File();
|
||||||
const char *get_filepath();
|
const char *get_filepath();
|
||||||
bool exists();
|
bool exists();
|
||||||
bool open(OpenMode);
|
bool open(OpenMode);
|
||||||
|
bool close();
|
||||||
|
|
||||||
const File &operator<<(File &);
|
const File &operator<<(File &);
|
||||||
const File &operator>>(File &);
|
const File &operator>>(File &);
|
||||||
|
@ -79,6 +82,9 @@ On-disk files: (lib/file.cpp)
|
||||||
$HOME/.ocarina/
|
$HOME/.ocarina/
|
||||||
$HOME/.ocarina-debug/
|
$HOME/.ocarina-debug/
|
||||||
|
|
||||||
|
File : ~File()
|
||||||
|
Close the file stream if it is open.
|
||||||
|
|
||||||
const char *get_filepath()
|
const char *get_filepath()
|
||||||
Return the full filepath to the file.
|
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
|
- Write version information to the start of the file
|
||||||
- Return true
|
- 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<<()
|
File : operator<<()
|
||||||
Write data to the file.
|
Write data to the file.
|
||||||
|
|
|
@ -4,22 +4,42 @@
|
||||||
#ifndef OCARINA_FILE_H
|
#ifndef OCARINA_FILE_H
|
||||||
#define OCARINA_FILE_H
|
#define OCARINA_FILE_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define FILE_VERSION 0
|
||||||
|
|
||||||
enum FileLocHint {
|
enum FileLocHint {
|
||||||
FILE_TYPE_CONFIG,
|
FILE_TYPE_CONFIG,
|
||||||
FILE_TYPE_DATA,
|
FILE_TYPE_DATA,
|
||||||
FILE_TYPE_LEGACY,
|
FILE_TYPE_LEGACY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum OpenMode {
|
||||||
|
OPEN_READ,
|
||||||
|
OPEN_WRITE,
|
||||||
|
NOT_OPEN,
|
||||||
|
};
|
||||||
|
|
||||||
class File {
|
class File {
|
||||||
private:
|
private:
|
||||||
|
std::fstream stream;
|
||||||
|
OpenMode mode;
|
||||||
FileLocHint hint;
|
FileLocHint hint;
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
|
unsigned int version;
|
||||||
|
|
||||||
|
void find_dir(std::string &);
|
||||||
|
bool open_read();
|
||||||
|
bool open_write();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File(std::string, FileLocHint);
|
File(std::string, FileLocHint);
|
||||||
|
~File();
|
||||||
const char *get_filepath();
|
const char *get_filepath();
|
||||||
|
bool exists();
|
||||||
|
bool open(OpenMode);
|
||||||
|
bool close();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* OCARINA_FILE_H */
|
#endif /* OCARINA_FILE_H */
|
||||||
|
|
9
include/test.h
Normal file
9
include/test.h
Normal file
|
@ -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")
|
CONFIG.package("glib-2.0")
|
||||||
build += [ env.Object("file.cpp") ]
|
build += [ env.Object("file.cpp") ]
|
||||||
|
|
||||||
|
if CONFIG.TEST:
|
||||||
|
build += [ env.Object("test.cpp") ]
|
||||||
|
|
||||||
Return("build")
|
Return("build")
|
||||||
|
|
128
lib/file.cpp
128
lib/file.cpp
|
@ -2,6 +2,7 @@
|
||||||
* Copyright 2013 (c) Bryan Schumaker.
|
* Copyright 2013 (c) Bryan Schumaker.
|
||||||
*/
|
*/
|
||||||
#include <file.h>
|
#include <file.h>
|
||||||
|
#include <print.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
@ -13,37 +14,118 @@
|
||||||
#define OCARINA_DIR "ocarina"
|
#define OCARINA_DIR "ocarina"
|
||||||
#endif
|
#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)
|
File :: File(std::string path, FileLocHint file_hint)
|
||||||
: hint(file_hint)
|
: mode(NOT_OPEN), hint(file_hint), version(FILE_VERSION)
|
||||||
{
|
{
|
||||||
std::string dir;
|
std::string dir;
|
||||||
|
|
||||||
switch (file_hint) {
|
find_dir(dir);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
filepath = dir + "/" + path;
|
filepath = dir + "/" + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File :: ~File()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
const char *File :: get_filepath()
|
const char *File :: get_filepath()
|
||||||
{
|
{
|
||||||
return filepath.c_str();
|
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();
|
||||||
|
}
|
||||||
|
|
27
lib/test.cpp
Normal file
27
lib/test.cpp
Normal file
|
@ -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
|
CONFIG.FILE = True
|
||||||
|
|
||||||
Test("file", "compile.cpp")
|
Test("file", "compile.cpp")
|
||||||
|
Test("file", "open.cpp")
|
||||||
|
|
22
tests/file/open-debug.good
Normal file
22
tests/file/open-debug.good
Normal file
|
@ -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
|
106
tests/file/open.cpp
Normal file
106
tests/file/open.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
18
tests/file/open.good
Normal file
18
tests/file/open.good
Normal file
|
@ -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
Block a user