Implement object model for files (#1532)

This commit is contained in:
Arnim Läuger 2016-11-08 21:02:51 +01:00 committed by Marcel Stör
parent b74a9dbdf7
commit a0e2e0ca37
4 changed files with 342 additions and 166 deletions

View File

@ -76,6 +76,9 @@ extern void luaL_assertfail(const char *file, int line, const char *message);
// maximum length of a filename // maximum length of a filename
#define FS_OBJ_NAME_LEN 31 #define FS_OBJ_NAME_LEN 31
// maximum number of open files for SPIFFS
#define SPIFFS_MAX_OPEN_FILES 4
// Uncomment this next line for fastest startup // Uncomment this next line for fastest startup
// It reduces the format time dramatically // It reduces the format time dramatically
// #define SPIFFS_MAX_FILESYSTEM_SIZE 32768 // #define SPIFFS_MAX_FILESYSTEM_SIZE 32768

View File

@ -14,8 +14,12 @@
#define FILE_READ_CHUNK 1024 #define FILE_READ_CHUNK 1024
static int file_fd = 0; static int file_fd = 0;
static int file_fd_ref = LUA_NOREF;
static int rtc_cb_ref = LUA_NOREF; static int rtc_cb_ref = LUA_NOREF;
typedef struct _file_fd_ud {
int fd;
} file_fd_ud;
static void table2tm( lua_State *L, vfs_time *tm ) static void table2tm( lua_State *L, vfs_time *tm )
{ {
@ -96,13 +100,48 @@ static int file_on(lua_State *L)
// Lua: close() // Lua: close()
static int file_close( lua_State* L ) static int file_close( lua_State* L )
{ {
if(file_fd){ int need_pop = FALSE;
vfs_close(file_fd); file_fd_ud *ud;
file_fd = 0;
if (lua_type( L, 1 ) != LUA_TUSERDATA) {
// fall back to last opened file
if (file_fd_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
// top of stack is now default file descriptor
ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
lua_pop( L, 1 );
} else {
// no default file currently opened
return 0;
}
} else {
ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
}
// unref default file descriptor
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
file_fd_ref = LUA_NOREF;
if(ud->fd){
vfs_close(ud->fd);
// mark as closed
ud->fd = 0;
} }
return 0; return 0;
} }
static int file_obj_free( lua_State *L )
{
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
if (ud->fd) {
// close file if it's still open
vfs_close(ud->fd);
ud->fd = 0;
}
return 0;
}
// Lua: format() // Lua: format()
static int file_format( lua_State* L ) static int file_format( lua_State* L )
{ {
@ -135,10 +174,10 @@ static int file_fscfg (lua_State *L)
static int file_open( lua_State* L ) static int file_open( lua_State* L )
{ {
size_t len; size_t len;
if(file_fd){
vfs_close(file_fd); // unref last file descriptor to allow gc'ing if not kept by user script
file_fd = 0; luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
} file_fd_ref = LUA_NOREF;
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname ); const char *basename = vfs_basename( fname );
@ -151,7 +190,14 @@ static int file_open( lua_State* L )
if(!file_fd){ if(!file_fd){
lua_pushnil(L); lua_pushnil(L);
} else { } else {
lua_pushboolean(L, 1); file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
ud->fd = file_fd;
luaL_getmetatable( L, "file.obj" );
lua_setmetatable( L, -2 );
// store reference to opened file
lua_pushvalue( L, -1 );
file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
} }
return 1; return 1;
} }
@ -175,19 +221,36 @@ static int file_list( lua_State* L )
return 0; return 0;
} }
static int file_seek (lua_State *L) static int get_file_obj( lua_State *L, int *argpos )
{ {
if (lua_type( L, 1 ) == LUA_TUSERDATA) {
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
*argpos = 2;
return ud->fd;
} else {
*argpos = 1;
return file_fd;
}
}
#define GET_FILE_OBJ int argpos; \
int fd = get_file_obj( L, &argpos );
static int file_seek (lua_State *L)
{
GET_FILE_OBJ;
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END}; static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL}; static const char *const modenames[] = {"set", "cur", "end", NULL};
if(!file_fd) if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
int op = luaL_checkoption(L, 1, "cur", modenames); int op = luaL_checkoption(L, argpos, "cur", modenames);
long offset = luaL_optlong(L, 2, 0); long offset = luaL_optlong(L, ++argpos, 0);
op = vfs_lseek(file_fd, offset, mode[op]); op = vfs_lseek(fd, offset, mode[op]);
if (op < 0) if (op < 0)
lua_pushnil(L); /* error */ lua_pushnil(L); /* error */
else else
lua_pushinteger(L, vfs_tell(file_fd)); lua_pushinteger(L, vfs_tell(fd));
return 1; return 1;
} }
@ -215,7 +278,6 @@ static int file_remove( lua_State* L )
const char *fname = luaL_checklstring( L, 1, &len ); const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname ); const char *basename = vfs_basename( fname );
luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid"); luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
file_close(L);
vfs_remove((char *)fname); vfs_remove((char *)fname);
return 0; return 0;
} }
@ -223,9 +285,11 @@ static int file_remove( lua_State* L )
// Lua: flush() // Lua: flush()
static int file_flush( lua_State* L ) static int file_flush( lua_State* L )
{ {
if(!file_fd) GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
if(vfs_flush(file_fd) == 0) if(vfs_flush(fd) == 0)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
lua_pushnil(L); lua_pushnil(L);
@ -236,10 +300,6 @@ static int file_flush( lua_State* L )
static int file_rename( lua_State* L ) static int file_rename( lua_State* L )
{ {
size_t len; size_t len;
if(file_fd){
vfs_close(file_fd);
file_fd = 0;
}
const char *oldname = luaL_checklstring( L, 1, &len ); const char *oldname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( oldname ); const char *basename = vfs_basename( oldname );
@ -258,12 +318,14 @@ static int file_rename( lua_State* L )
} }
// g_read() // g_read()
static int file_g_read( lua_State* L, int n, int16_t end_char ) static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
{ {
static char *heap_mem = NULL; static char *heap_mem = NULL;
// free leftover memory // free leftover memory
if (heap_mem) if (heap_mem) {
luaM_free(L, heap_mem); luaM_free(L, heap_mem);
heap_mem = NULL;
}
if(n <= 0) if(n <= 0)
n = FILE_READ_CHUNK; n = FILE_READ_CHUNK;
@ -271,7 +333,8 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
if(end_char < 0 || end_char >255) if(end_char < 0 || end_char >255)
end_char = EOF; end_char = EOF;
if(!file_fd)
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
char *p; char *p;
@ -285,7 +348,7 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
p = alloca(n); p = alloca(n);
} }
n = vfs_read(file_fd, p, n); n = vfs_read(fd, p, n);
// bypass search if no end character provided // bypass search if no end character provided
for (i = end_char != EOF ? 0 : n; i < n; ++i) for (i = end_char != EOF ? 0 : n; i < n; ++i)
if (p[i] == end_char) if (p[i] == end_char)
@ -302,7 +365,7 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
return 0; return 0;
} }
vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR); vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
lua_pushlstring(L, p, i); lua_pushlstring(L, p, i);
if (heap_mem) { if (heap_mem) {
luaM_free(L, heap_mem); luaM_free(L, heap_mem);
@ -320,36 +383,43 @@ static int file_read( lua_State* L )
unsigned need_len = FILE_READ_CHUNK; unsigned need_len = FILE_READ_CHUNK;
int16_t end_char = EOF; int16_t end_char = EOF;
size_t el; size_t el;
if( lua_type( L, 1 ) == LUA_TNUMBER )
GET_FILE_OBJ;
if( lua_type( L, argpos ) == LUA_TNUMBER )
{ {
need_len = ( unsigned )luaL_checkinteger( L, 1 ); need_len = ( unsigned )luaL_checkinteger( L, argpos );
} }
else if(lua_isstring(L, 1)) else if(lua_isstring(L, argpos))
{ {
const char *end = luaL_checklstring( L, 1, &el ); const char *end = luaL_checklstring( L, argpos, &el );
if(el!=1){ if(el!=1){
return luaL_error( L, "wrong arg range" ); return luaL_error( L, "wrong arg range" );
} }
end_char = (int16_t)end[0]; end_char = (int16_t)end[0];
} }
return file_g_read(L, need_len, end_char); return file_g_read(L, need_len, end_char, fd);
} }
// Lua: readline() // Lua: readline()
static int file_readline( lua_State* L ) static int file_readline( lua_State* L )
{ {
return file_g_read(L, FILE_READ_CHUNK, '\n'); GET_FILE_OBJ;
return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd);
} }
// Lua: write("string") // Lua: write("string")
static int file_write( lua_State* L ) static int file_write( lua_State* L )
{ {
if(!file_fd) GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
size_t l, rl; size_t l, rl;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(file_fd, s, l); rl = vfs_write(fd, s, l);
if(rl==l) if(rl==l)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
@ -360,13 +430,15 @@ static int file_write( lua_State* L )
// Lua: writeline("string") // Lua: writeline("string")
static int file_writeline( lua_State* L ) static int file_writeline( lua_State* L )
{ {
if(!file_fd) GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
size_t l, rl; size_t l, rl;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(file_fd, s, l); rl = vfs_write(fd, s, l);
if(rl==l){ if(rl==l){
rl = vfs_write(file_fd, "\n", 1); rl = vfs_write(fd, "\n", 1);
if(rl==1) if(rl==1)
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
else else
@ -441,6 +513,20 @@ static int file_vol_umount( lua_State *L )
} }
static const LUA_REG_TYPE file_obj_map[] =
{
{ LSTRKEY( "close" ), LFUNCVAL( file_close ) },
{ LSTRKEY( "read" ), LFUNCVAL( file_read ) },
{ LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
{ LSTRKEY( "write" ), LFUNCVAL( file_write ) },
{ LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
{ LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
{ LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) },
{ LSTRKEY( "__index" ), LROVAL( file_obj_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE file_vol_map[] = static const LUA_REG_TYPE file_vol_map[] =
{ {
{ LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )}, { LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )},
@ -480,6 +566,7 @@ static const LUA_REG_TYPE file_map[] = {
int luaopen_file( lua_State *L ) { int luaopen_file( lua_State *L ) {
luaL_rometatable( L, "file.vol", (void *)file_vol_map ); luaL_rometatable( L, "file.vol", (void *)file_vol_map );
luaL_rometatable( L, "file.obj", (void *)file_obj_map );
return 0; return 0;
} }

View File

@ -2,6 +2,8 @@
#include "platform.h" #include "platform.h"
#include "spiffs.h" #include "spiffs.h"
#include "spiffs_nucleus.h"
spiffs fs; spiffs fs;
#define LOG_PAGE_SIZE 256 #define LOG_PAGE_SIZE 256
@ -10,9 +12,9 @@ spiffs fs;
#define MIN_BLOCKS_FS 4 #define MIN_BLOCKS_FS 4
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[32*4]; static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES];
#if SPIFFS_CACHE #if SPIFFS_CACHE
static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*2]; static u8_t myspiffs_cache[(LOG_PAGE_SIZE+32)*2];
#endif #endif
static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
@ -168,8 +170,8 @@ static bool myspiffs_mount_internal(bool force_mount) {
spiffs_fds, spiffs_fds,
sizeof(spiffs_fds), sizeof(spiffs_fds),
#if SPIFFS_CACHE #if SPIFFS_CACHE
spiffs_cache, myspiffs_cache,
sizeof(spiffs_cache), sizeof(myspiffs_cache),
#else #else
0, 0, 0, 0,
#endif #endif

View File

@ -7,8 +7,6 @@ The file module provides access to the file system and its individual files.
The file system is a flat file system, with no notion of subdirectories/folders. The file system is a flat file system, with no notion of subdirectories/folders.
Only one file can be open at any given time.
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md). Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md).
```lua ```lua
@ -43,30 +41,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste
#### Returns #### Returns
`true` on success, `false` otherwise `true` on success, `false` otherwise
## file.close()
Closes the open file, if any.
#### Syntax
`file.close()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
-- open 'init.lua', print the first line.
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
[`file.open()`](#fileopen)
## file.exists() ## file.exists()
Determines whether the specified file exists. Determines whether the specified file exists.
@ -95,34 +69,6 @@ end
#### See also #### See also
[`file.list()`](#filelist) [`file.list()`](#filelist)
## file.flush()
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well.
#### Syntax
`file.flush()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
-- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.flush()
-- write 'baz' too
file.write('baz')
file.close()
end
```
#### See also
[`file.close()`](#fileclose)
## file.format() ## file.format()
Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds. Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds.
@ -280,9 +226,9 @@ When done with the file, it must be closed using `file.close()`.
- "a+": append update mode, previous data is preserved, writing is only allowed at the end of file - "a+": append update mode, previous data is preserved, writing is only allowed at the end of file
#### Returns #### Returns
`nil` if file not opened, or not exists (read modes). `true` if file opened ok. file object if file opened ok. `nil` if file not opened, or not exists (read modes).
#### Example #### Example (basic model)
```lua ```lua
-- open 'init.lua', print the first line. -- open 'init.lua', print the first line.
if file.open("init.lua", "r") then if file.open("init.lua", "r") then
@ -290,74 +236,19 @@ if file.open("init.lua", "r") then
file.close() file.close()
end end
``` ```
#### See also #### Example (object model)
- [`file.close()`](#fileclose)
- [`file.readline()`](#filereadline)
## file.read()
Read content from the open file.
!!! note
The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`.
#### Syntax
`file.read([n_or_char])`
#### Parameters
- `n_or_char`:
- if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller).
- if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller).
- if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached.
#### Returns
File content as a string, or nil when EOF
#### Example
```lua ```lua
-- print the first line of 'init.lua' -- open 'init.lua', print the first line.
if file.open("init.lua", "r") then fd = file.open("init.lua", "r")
print(file.read('\n')) if fd then
file.close() print(fd:readline())
end fd:close(); fd = nil
-- print the first 5 bytes of 'init.lua'
if file.open("init.lua", "r") then
print(file.read(5))
file.close()
end end
``` ```
#### See also #### See also
- [`file.open()`](#fileopen)
- [`file.readline()`](#filereadline)
## file.readline()
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes.
#### Syntax
`file.readline()`
#### Parameters
none
#### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.close()`](#fileclose) - [`file.close()`](#fileclose)
- [`file.read()`](#filereade) - [`file.readline()`](#filereadline)
## file.remove() ## file.remove()
@ -402,12 +293,188 @@ Renames a file. If a file is currently open, it will be closed first.
file.rename("temp.lua","init.lua") file.rename("temp.lua","init.lua")
``` ```
# File access functions
The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model:
## Basic model
In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand.
```lua
-- open 'init.lua', print the first line.
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
## Object model
Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist.
```lua
src = file.open("init.lua", "r")
if src then
dest = file.open("copy.lua", "w")
if dest then
local line
repeat
line = src:read()
if line then
dest:write(line)
end
until line == nil
dest:close(); dest = nil
end
src:close(); dest = nil
end
```
!!! Attention
It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the correspoding file object. Closing a file from object model will also close the default file if they are the same file.
!!! Note
The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`.
## file.close()
## file.obj:close()
Closes the open file, if any.
#### Syntax
`file.close()`
`fd:close()`
#### Parameters
none
#### Returns
`nil`
#### See also
[`file.open()`](#fileopen)
## file.flush()
## file.obj:flush()
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose) performs an implicit flush as well.
#### Syntax
`file.flush()`
`fd:flush()`
#### Parameters
none
#### Returns
`nil`
#### Example (basic model)
```lua
-- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.flush()
-- write 'baz' too
file.write('baz')
file.close()
end
```
#### See also
[`file.close()` / `file.obj:close()`](#fileclose)
## file.read()
## file.obj:read()
Read content from the open file.
!!! note
The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`.
#### Syntax
`file.read([n_or_char])`
`fd:read([n_or_char])`
#### Parameters
- `n_or_char`:
- if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller).
- if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller).
- if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached.
#### Returns
File content as a string, or nil when EOF
#### Example (basic model)
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.read('\n'))
file.close()
end
```
#### Example (object model)
```lua
-- print the first 5 bytes of 'init.lua'
fd = file.open("init.lua", "r")
if fd then
print(fd:read(5))
fd:close(); fd = nil
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.readline()` / `file.obj:readline()`](#filereadline)
## file.readline()
## file.obj:readline()
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes.
#### Syntax
`file.readline()`
`fd:readline()`
#### Parameters
none
#### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example (basic model)
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.close()` / `file.obj:close()`](#fileclose)
- [`file.read()` / `file.obj:read()`](#fileread)
## file.seek() ## file.seek()
## file.obj:seek()
Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence. Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence.
#### Syntax #### Syntax
`file.seek([whence [, offset]])` `file.seek([whence [, offset]])`
`fd:seek([whence [, offset]])`
#### Parameters #### Parameters
- `whence` - `whence`
- "set": base is position 0 (beginning of the file) - "set": base is position 0 (beginning of the file)
@ -420,7 +487,7 @@ If no parameters are given, the function simply returns the current file offset.
#### Returns #### Returns
the resulting file position, or `nil` on error the resulting file position, or `nil` on error
#### Example #### Example (basic model)
```lua ```lua
if file.open("init.lua", "r") then if file.open("init.lua", "r") then
-- skip the first 5 bytes of the file -- skip the first 5 bytes of the file
@ -433,19 +500,22 @@ end
[`file.open()`](#fileopen) [`file.open()`](#fileopen)
## file.write() ## file.write()
## file.obj:write()
Write a string to the open file. Write a string to the open file.
#### Syntax #### Syntax
`file.write(string)` `file.write(string)`
`fd:write(string)`
#### Parameters #### Parameters
`string` content to be write to file `string` content to be write to file
#### Returns #### Returns
`true` if the write is ok, `nil` on error `true` if the write is ok, `nil` on error
#### Example #### Example (basic model)
```lua ```lua
-- open 'init.lua' in 'a+' mode -- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then if file.open("init.lua", "a+") then
@ -455,24 +525,38 @@ if file.open("init.lua", "a+") then
end end
``` ```
#### Example (object model)
```lua
-- open 'init.lua' in 'a+' mode
fd = file.open("init.lua", "a+")
if fd then
-- write 'foo bar' to the end of the file
fd:write('foo bar')
fd:close()
end
```
#### See also #### See also
- [`file.open()`](#fileopen) - [`file.open()`](#fileopen)
- [`file.writeline()`](#filewriteline) - [`file.writeline()` / `file.obj:writeline()`](#filewriteline)
## file.writeline() ## file.writeline()
## file.obj:writeline()
Write a string to the open file and append '\n' at the end. Write a string to the open file and append '\n' at the end.
#### Syntax #### Syntax
`file.writeline(string)` `file.writeline(string)`
`fd:writeline(string)`
#### Parameters #### Parameters
`string` content to be write to file `string` content to be write to file
#### Returns #### Returns
`true` if write ok, `nil` on error `true` if write ok, `nil` on error
#### Example #### Example (basic model)
```lua ```lua
-- open 'init.lua' in 'a+' mode -- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then if file.open("init.lua", "a+") then
@ -484,4 +568,4 @@ end
#### See also #### See also
- [`file.open()`](#fileopen) - [`file.open()`](#fileopen)
- [`file.readline()`](#filereadline) - [`file.readline()` / `file.obj:readline()`](#filereadline)