/**
@file posix.c
@brief POSIX wrapper for FatFS
- Provides many of the common Posix/linux functions
- POSIX character I/O functions
- isatty
- fgetc
- fputc
- getchar
- putc
- putchar
- ungetc
- POSIX string I/O functions
- fgets
- fputs
- puts
- POSIX file position functions
- feof
- fgetpos
- fseek
- fsetpos
- ftell
- lseek
- rewind
- POSIX file functions
- close
- fileno
- fileno_to_stream NOT POSIX
- fopen
- fread
- ftruncate
- fwrite
- open
- read
- sync
- syncfs
- truncate
- write
- fclose
- POSIX file information functions
- dump_stat - NOT POSIX
- fstat
- mctime - NOT POSIX
- stat
- POSIX file and directory manipulation
- basename
- baseext - NOT POSIX
- chmod
- chdir
- dirname
- getcwd
- mkdir
- rename
- rmdir
- unlink
- utime
- POSIX - directory scanning functions
- closedir
- opendir
- readdir
- POSIX error functions
- clrerror
- ferror
- perror
- strerror
- strerror_r
- Device open functions
- fdevopen - NOT POSIX
- FatFS to POSIX bridge functions - NOT POSIX
- fatfs_getc
- fatfs_putc
- fatfs_to_errno
- fatfs_to_fileno
- fat_time_to_unix
- fileno_to_fatfs
- free_file_descriptor
- new_file_descriptor
- mkfs
- posix_fopen_modes_to_open
- unix_time_to_fat
@par Copyright © 2015 Mike Gore, GPL License
@par You are free to use this code under the terms of GPL
please retain a copy of this notice in any code you use it in.
This 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 3 of the License, or (at your option)
any later version.
This software 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, see .
*/
#include
#include "posix.h"
#undef strerror_r
/// Note: fdevopen assigns stdin,stdout,stderr
///@brief POSIX errno.
int errno;
///@brief POSIX fileno to POSIX FILE stream table
///
/// - Note: the index of __iob[] is reffered to "fileno".
/// - Reference: libc/avr-libc-1.8.0/libc/stdio.
/// - stdin = __iob[0].
/// - __iob[1] = stdout.
/// - __iob[2] = stderr.
FILE *__iob[MAX_FILES];
/// @brief POSIX error messages for each errno value.
///
/// - man page errno (3)
const char *sys_errlist[] =
{
"OK",
"Operation not permitted",
"No such file or directory",
"No such process",
"Interrupted system call",
"I/O error",
"No such device or address",
"Argument list too long",
"Exec format error",
"Bad file number",
"No child processes",
"Try again",
"Out of memory",
"Permission denied",
"Bad address",
"Block device required",
"Device or resource busy",
"File exists",
"Cross-device link",
"No such device",
"Not a directory",
"Is a directory",
"Invalid argument",
"File table overflow",
"Too many open files",
"Not a typewriter",
"Text file busy",
"File too large",
"No space left on device",
"Illegal seek",
"Read-only file system",
"Too many links",
"Broken pipe",
"Math argument out of domain of func",
"Math result not representable",
"Bad Message",
NULL
};
// =============================================
/// - POSIX character I/O functions
/// @brief Test POSIX fileno if it is a Serial Console/TTY.
///
/// - man page isatty (3).
///
/// @param[in] fileno: POSIX fileno of open file.
///
/// @return 1 if fileno is a serial TTY/Console (uart in avr-libc terms).
/// @return 0 if POSIX fileno is NOT a Serial TTY.
int isatty(int fileno)
{
/// @todo Perhaps we should verify console functions have been added ?
if(fileno >= 0 && fileno <= 2)
return(1);
return 0;
}
/// @brief Get byte from a TTY device or FatFs file stream
/// open() or fopen() sets stream->get = fatfs_getc() for FatFs functions
/// See fdevopen() sets stream->get for TTY devices
///
/// - man page fgetc (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
/// @return EOF on error with errno set.
int
fgetc(FILE *stream)
{
int c;
if(stream == NULL)
{
errno = EBADF; // Bad File Number
return(EOF);
}
if ((stream->flags & __SRD) == 0)
return EOF;
if ((stream->flags & __SUNGET) != 0) {
stream->flags &= ~__SUNGET;
stream->len++;
return stream->unget;
}
if (stream->flags & __SSTR) {
c = *stream->buf;
if (c == '\0') {
stream->flags |= __SEOF;
return EOF;
} else {
stream->buf++;
}
} else {
if(!stream->get)
{
printf("fgetc stream->get NULL\n");
return(EOF);
}
// get character from device or file
c = stream->get(stream);
if (c < 0) {
/* if != _FDEV_ERR, assume its _FDEV_EOF */
stream->flags |= (c == _FDEV_ERR)? __SERR: __SEOF;
return EOF;
}
}
stream->len++;
return (c);
}
/// @brief Put a byte to TTY device or FatFs file stream
/// open() or fopen() sets stream->put = fatfs_outc() for FatFs functions
/// See fdevopen() sets stream->put get for TTY devices
///
/// - man page fputc (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
int
fputc(int c, FILE *stream)
{
errno = 0;
int ret;
if(stream == NULL)
{
errno = EBADF; // Bad File Number
return(EOF);
}
if(stream != stdout && stream != stderr)
{
return(fatfs_putc(c,stream));
}
// TTY outputs
if ((stream->flags & __SWR) == 0)
return EOF;
if (stream->flags & __SSTR) {
if (stream->len < stream->size)
*stream->buf++ = c;
stream->len++;
return c;
} else {
if(!stream->put)
{
printf("fputc stream->put NULL\n");
return(EOF);
}
ret = stream->put(c, stream);
if(ret != EOF)
stream->len++;
return(ret);
}
}
///@brief functions normally defined as macros
#ifndef IO_MACROS
/// @brief get a character from stdin
/// See fdevopen() sets stream->get for TTY devices
///
/// - man page getchar (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
int
getchar()
{
return(fgetc(stdin));
}
/// @brief put a character to stdout
/// See fdevopen() sets stream->put get for TTY devices
///
/// - man page putchar (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
int
putchar(int c)
{
return(fputc(c,stdout));
}
#endif
/// @brief Un-Get byte from a TTY device or FatFs file stream
///
/// - man page ungetc (3).
///
/// @param[in] c: Character to unget
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
/// @return EOF on error with errno set.
/*
int
ungetc(int c, FILE *stream)
{
int fd = fileno(stream);
if(!isatty(fd))
return(EOF);
if(c == EOF)
return EOF;
if((stream->flags & __SUNGET) != 0 )
return EOF;
if ((stream->flags & __SRD) == 0 )
return EOF;
stream->flags |= __SUNGET;
stream->flags &= ~__SEOF;
stream->unget = c;
stream->len--;
return (c);
}
*/
#ifndef IO_MACROS
// =============================================
/// @brief Put a character to a stream
/// See fdevopen() sets stream->put get for TTY devices
///
/// - man page putc (3).
///
/// @param[in] stream: POSIX stream pointer.
///
int
putc(int c, FILE *stream)
{
return(fputc(c, stream));
}
#endif
// =============================================
/// POSIX string I/O
/// @brief get a string from stdin
/// See fdevopen() sets stream->put get for TTY devices
///
/// - man page fgets (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
char *
fgets(char *str, int size, FILE *stream)
{
int c;
int ind = 0;
while(size--)
{
c = fgetc(stream);
if(c == EOF)
{
if( ind == 0)
return(NULL);
break;
}
if(c == '\n')
break;
if(c == 0x08)
{
if(ind > 0)
--ind;
continue;
}
str[ind++] = c;
}
str[ind] = 0;
return(str);
}
/// @brief put a string to stdout
/// See fdevopen() sets stream->put get for TTY devices
///
/// - man page fputs (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
int
fputs(const char *str, FILE *stream)
{
while(*str)
{
if(fputc(*str, stream) == EOF)
return(EOF);
++str;
}
return(0);
}
#ifndef IO_MACROS
/// @brief put a string to stdout
/// See fdevopen() sets stream->put get for TTY devices
///
/// - man page puts (3).
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
int
puts(const char *str)
{
while(*str)
{
if(fputc(*str, stdout) == EOF)
return(EOF);
++str;
}
return ( fputc('\n',stdout) );
}
#endif
// =============================================
// =============================================
/// - POSIX file position functions
// =============================================
// =============================================
/// @brief feof reports if the stream is at EOF
/// - man page feof (3).
///
/// @param[in] stream: POSIX stream pointer.
/// @return 1 if EOF set, 0 otherwise.
int feof(FILE *stream)
{
if(stream->flags & __SEOF)
return(1);
return(0);
}
/// @brief POSIX get position of file stream.
///
/// - man page fgetpos (3).
///
/// @param[in] stream: POSIX file stream.
/// @param[in] pos: position pointer for return.
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int fgetpos(FILE *stream, size_t *pos)
{
long offset = ftell(stream);
*pos = offset;
if(offset == -1)
return(-1);
return( 0 );
}
/// @brief POSIX seek to file possition.
///
/// - man page fseek (3).
///
/// @param[in] stream: POSIX file stream.
/// @param[in] offset: offset to seek to.
/// @param[in] whence:
/// - SEEK_SET The offset is set to offset bytes.
/// - SEEK_CUR The offset is set to its current location plus offset bytes.
/// - SEEK_END The offset is set to the size of the file plus offset bytes.
///
/// @return file position on sucess.
/// @return -1 on error.
int fseek(FILE *stream, long offset, int whence)
{
long ret;
int fn = fileno(stream);
if(fn < 0)
return(-1);
ret = lseek(fn, offset, whence);
if(ret == -1)
return(-1);
return(0);
}
/// @brief POSIX set position of file stream.
///
/// - man page fsetpos (3).
///
/// @param[in] stream: POSIX file stream.
/// @param[in] pos: position pointer.
///
/// @return 0 with *pos set to position on sucess.
/// @return -1 on error with errno set.
int fsetpos(FILE *stream, size_t *pos)
{
return (fseek(stream, (size_t) *pos, SEEK_SET) );
}
/// @brief POSIX file position of open stream.
///
/// - man page fteel (3).
///
/// @param[in] stream: POSIX file stream.
///
/// @return file position on sucess.
/// @return -1 on error with errno set.
long ftell(FILE *stream)
{
errno = 0;
int fn = fileno(stream);
if(isatty(fn))
return(-1);
// fileno_to_fatfs checks for fd out of bounds
FIL *fh = fileno_to_fatfs(fn);
if ( fh == NULL )
{
errno = EBADF;
return(-1);
}
return( fh->fptr );
}
/// @brief POSIX seek to file position.
///
/// - man page lseek (2).
///
/// @param[in] fileno: POSIX fileno of open file.
/// @param[in] position: offset to seek to.
/// @param[in] whence
/// - SEEK_SET The offset is set to offset bytes.
/// - SEEK_CUR The offset is set to its current location plus offset bytes.
/// - SEEK_END The offset is set to the size of the file plus offset bytes.
///
/// @return file position on sucess.
/// @return -1 on error.
off_t lseek(int fileno, off_t position, int whence)
{
FRESULT res;
FIL *fh;
errno = 0;
FILE *stream;
// fileno_to_fatfs checks for fd out of bounds
fh = fileno_to_fatfs(fileno);
if(fh == NULL)
{
errno = EMFILE;
return(-1);
}
if(isatty(fileno))
return(-1);
stream = fileno_to_stream(fileno);
stream->flags |= __SUNGET;
if(whence == SEEK_END)
position += f_size(fh);
else if(whence==SEEK_CUR)
position += fh->fptr;
res = f_lseek(fh, position);
if(res)
{
errno = fatfs_to_errno(res);
return -1;
}
return (fh->fptr);
}
/// @brief POSIX rewind file to the beginning.
///
/// - man page rewind (3).
///
/// @param[in] stream: POSIX file stream.
///
/// @return void.
void rewind( FILE *stream)
{
fseek(stream, 0L, SEEK_SET);
}
// =============================================
// =============================================
/// - POSIX file functions
// =============================================
// =============================================
/// @brief POSIX Close a file with fileno handel.
///
/// - man page close (2).
///
/// @param[in] fileno: fileno of file.
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int close(int fileno)
{
FILE *stream;
FIL *fh;
int res;
errno = 0;
// checks if fileno out of bounds
stream = fileno_to_stream(fileno);
if(stream == NULL)
{
return(-1);
}
// fileno_to_fatfs checks for fileno out of bounds
fh = fileno_to_fatfs(fileno);
if(fh == NULL)
{
return(-1);
}
res = f_close(fh);
free_file_descriptor(fileno);
if (res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
/// @brief Convert POSIX stream pointer to POSIX fileno (index of __iob[])
///
/// - man page fileno (3)
/// @param[in] stream: stream pointer
///
/// @return int fileno on success
/// @return -1 with errno = EBAFD if stream is NULL or not found
int fileno(FILE *stream)
{
int fileno;
if(stream == NULL)
{
errno = EBADF;
return(-1);
}
for(fileno=0; fileno= MAX_FILES)
{
errno = EBADF;
return(NULL);
}
stream = __iob[fileno];
if(stream == NULL)
{
errno = EBADF;
return(NULL);
}
return(stream);
}
///@brief POSIX Open a file with path name and ascii file mode string.
///
/// - man page fopen(3).
///
/// @param[in] path: filename string.
/// @param[in] mode: POSIX open mode strings.
///
/// @return stream * on success.
/// @return NULL on error with errno set.
FILE *fopen(const char *path, const char *mode)
{
int flags = posix_fopen_modes_to_open(mode);
int fileno = open(path, flags);
// checks if fileno out of bounds
return( fileno_to_stream(fileno) );
}
/// @brief POSIX read nmemb elements from buf, size bytes each, to the stream fd.
///
/// - man page fread (3).
///
/// @param[in] ptr: buffer.
/// @param[in] nmemb: number of items to read.
/// @param[in] size: size of each item in bytes.
/// @param[in] stream: POSIX file stream.
///
/// @return count on sucess.
/// @return 0 or < size on error with errno set.
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t count = size * nmemb;
int fn = fileno(stream);
ssize_t ret;
// read() checks for fn out of bounds
ret = read(fn, ptr, count);
if(ret < 0)
return(0);
return((size_t) ret);
}
/// @brief POSIX truncate open file to length.
///
/// - man page ftruncate (3).
///
/// @param[in] fd: open file number.
/// @param[in] length: length to truncate to.
///
/// @return 0 on success.
/// @return -1 on fail.
int ftruncate(int fd, off_t length)
{
errno = 0;
FIL *fh;
FRESULT rc;
if(isatty(fd))
return(-1);
// fileno_to_fatfs checks for fd out of bounds
fh = fileno_to_fatfs(fd);
if(fh == NULL)
{
return(-1);
}
rc = f_lseek(fh, length);
if (rc != FR_OK)
{
errno = fatfs_to_errno(rc);
return(-1);
}
rc = f_truncate(fh);
if (rc != FR_OK)
{
errno = fatfs_to_errno(rc);
return(-1);
}
return(0);
}
/// @brief POSIX write nmemb elements from buf, size bytes each, to the stream fd.
///
/// - man page write (2).
///
/// @param[in] ptr: buffer.
/// @param[in] nmemb: number of items to write.
/// @param[in] size: size of each item in bytes.
/// @param[in] stream: POSIX file stream.
///
/// @return count written on sucess.
/// @return 0 or < size on error with errno set.
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t count = size * nmemb;
int fn = fileno(stream);
ssize_t ret;
// write () checks for fn out of bounds
ret = write(fn, ptr, count);
if(ret < 0)
return(0);
return((size_t) ret);
}
/// @brief POSIX Open a file with integer mode flags.
///
/// - man page open (2).
///
/// @param[in] pathname: filename string.
/// @param[in] flags: POSIX open modes.
///
/// @return fileno on success.
/// @return -1 on error with errno set.
int open(const char *pathname, int flags)
{
int fileno;
int fatfs_modes;
FILE *stream;
FIL *fh;
int res;
errno = 0;
// FIXME Assume here that the disk interface mmc_init was already called
#if 0
// Checks Disk status
res = mmc_init(0);
if(res != RES_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
#endif
if((flags & O_ACCMODE) == O_RDWR)
fatfs_modes = FA_READ | FA_WRITE;
else if((flags & O_ACCMODE) == O_RDONLY)
fatfs_modes = FA_READ;
else
fatfs_modes = FA_WRITE;
if(flags & O_CREAT)
{
if(flags & O_TRUNC)
fatfs_modes |= FA_CREATE_ALWAYS;
else
fatfs_modes |= FA_OPEN_ALWAYS;
}
fileno = new_file_descriptor();
// checks if fileno out of bounds
stream = fileno_to_stream(fileno);
if(stream == NULL)
{
free_file_descriptor(fileno);
return(-1);
}
// fileno_to_fatfs checks for fileno out of bounds
fh = fileno_to_fatfs(fileno);
if(fh == NULL)
{
free_file_descriptor(fileno);
errno = EBADF;
return(-1);
}
res = f_open(fh, pathname, (BYTE) (fatfs_modes & 0xff));
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
free_file_descriptor(fileno);
return(-1);
}
if(flags & O_APPEND)
{
/// Seek to end of the file
res = f_lseek(fh, f_size(fh));
if (res != FR_OK)
{
errno = fatfs_to_errno(res);
f_close(fh);
free_file_descriptor(fileno);
return(-1);
}
}
if((flags & O_ACCMODE) == O_RDWR)
{
// FIXME fdevopen should do this
stream->put = fatfs_putc;
stream->get = fatfs_getc;
stream->flags = _FDEV_SETUP_RW;
}
else if((flags & O_ACCMODE) == O_RDONLY)
{
// FIXME fdevopen should do this
stream->put = NULL;
stream->get = fatfs_getc;
stream->flags = _FDEV_SETUP_READ;
}
else
{
// FIXME fdevopen should do this
stream->put = fatfs_putc;
stream->get = NULL;
stream->flags = _FDEV_SETUP_WRITE;
}
return(fileno);
}
/// @brief POSIX read count bytes from *buf to fileno fd.
///
/// - man page read (2).
///
/// @param[in] fd: POSIX fileno.
/// @param[in] buf: buffer.
/// @param[in] count: number of bytes to write.
///
/// @return count on sucess.
/// @return -1 on error with errno set.
ssize_t read(int fd, void *buf, size_t count)
{
UINT size;
UINT bytes = count;
int res;
int ret;
FIL *fh;
FILE *stream;
//FIXME
*(char *) buf = 0;
errno = 0;
// TTY read function
// FIXME should we really be blocking ???
stream = fileno_to_stream(fd);
if(stream == stdin)
{
char *ptr = (char *) buf;
// ungetc is undefined for read
stream->flags |= __SUNGET;
size = 0;
while(count--)
{
ret = fgetc(stream);
if(ret < 0)
break;
*ptr++ = ret;
++size;
}
return(size);
}
if(stream == stdout || stream == stderr)
{
return(-1);
}
// fileno_to_fatfs checks for fd out of bounds
fh = fileno_to_fatfs(fd);
if ( fh == NULL )
{
errno = EBADF;
return(-1);
}
res = f_read(fh, (void *) buf, bytes, &size);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return ((ssize_t) size);
}
/// @brief POSIX Sync all pending file changes and metadata on ALL files.
///
/// - man page sync (2).
///
/// @return void.
void sync(void)
{
FIL *fh;
int i;
for(i=0;iflags |= __SUNGET;
// fileno_to_fatfs checks for fd out of bounds
fh = fileno_to_fatfs(fd);
if(fh == NULL)
{
errno = EBADF;
return(-1);
}
res = f_sync ( fh );
if (res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
/// @brief POSIX truncate named file to length.
///
/// - man page truncate (2).
///
/// @param[in] path: file name to truncate.
/// @param[in] length: length to truncate to.
///
/// @return 0 on sucess.
/// @return -1 n fail.
int truncate(const char *path, off_t length)
{
errno = 0;
FIL fh;
FRESULT rc;
rc = f_open(&fh , path, FA_OPEN_EXISTING | FA_READ | FA_WRITE);
if (rc != FR_OK)
{
errno = fatfs_to_errno(rc);
return(-1);
}
rc = f_lseek(&fh, length);
if (rc != FR_OK)
{
errno = fatfs_to_errno(rc);
return(-1);
}
rc = f_truncate(&fh);
if (rc != FR_OK)
{
errno = fatfs_to_errno(rc);
return(-1);
}
return(0);
}
/// @brief POSIX Write count bytes from *buf to fileno fd.
///
/// - man page write (2).
///
/// @param[in] fd: POSIX fileno.
/// @param[in] buf: buffer.
/// @param[in] count: number of bytes to write.
/// @return count on sucess.
/// @return -1 on error with errno set.
ssize_t write(int fd, const void *buf, size_t count)
{
UINT size;
UINT bytes = count;
FRESULT res;
FIL *fh;
FILE *stream;
errno = 0;
// TTY read function
stream = fileno_to_stream(fd);
if(stream == stdout || stream == stderr)
{
char *ptr = (char *) buf;
size = 0;
while(count--)
{
int c,ret;
c = *ptr++;
ret = fputc(c, stream);
if(c != ret)
break;
++size;
}
return(size);
}
if(stream == stdin)
{
return(-1);
}
// fileno_to_fatfs checks for fd out of bounds
fh = fileno_to_fatfs(fd);
if ( fh == NULL )
{
errno = EBADF;
return(-1);
}
res = f_write(fh, buf, bytes, &size);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return ((ssize_t) size);
}
/// @brief POSIX close a file stream.
///
/// - man page flose (3).
///
/// @param[in] stream: POSIX stream pointer.
/// @return 0 on sucess.
/// @return -1 on error witrh errno set.
/*
int fclose(FILE *stream)
{
int fn = fileno(stream);
if(fn < 0)
return(EOF);
return( close(fn) );
}
*/
// =============================================
// =============================================
/// - POSIX file information functions
// =============================================
// =============================================
/// @brief Display struct stat, from POSIX stat(0 or fstat(), in ASCII.
/// NOT POSIX
/// @param[in] sp: struct stat pointer.
///
/// @return void.
/*
void dump_stat(struct stat *sp)
{
mode_t mode = sp->st_mode;
printf("\tSize: %lu\n", (uint32_t)sp->st_size);
printf("\tType: ");
if(S_ISDIR(mode))
printf("DIR\n");
else if(S_ISREG(mode))
printf("File\n");
else
printf("Unknown\n");
printf("\tMode: %lo\n", (uint32_t)sp->st_mode);
printf("\tUID: %lu\n", (uint32_t)sp->st_uid);
printf("\tGID: %lu\n", (uint32_t)sp->st_gid);
printf("\tatime: %s\n",mctime((time_t)sp->st_atime));
printf("\tmtime: %s\n",mctime((time_t)sp->st_mtime));
printf("\tctime: %s\n",mctime((time_t)sp->st_ctime));
}
*/
#if 0
/// @brief POSIX fstat of open file.
/// FatFS does not have a function that will map to this
///
/// - man page (2).
///
/// @param[in] fd: POSIX fileno of open file.
/// @param[in] buf: struct stat buffer to return results in.
///
/// @return 0 on success.
/// @return -1 on error with errno set.
///
/// @todo needs fileno to filename lookup in order to work.
/// - We may be able to work out the directory pointer from the FatFS data?
/// - or - cache the filename on open ???
int fstat(int fd, struct stat *buf)
{
FIL *fh;
FRESULT rc;
if(isatty(fd))
return(-1);
//FIXME TODO
return(-1);
}
#endif
/// @brief POSIX stat - get file status of named file.
///
/// - man page (2).
///
/// @param[in] name: file name.
/// @param[in] buf: struct stat buffer to return results in.
///
/// @return 0 on success.
/// @return -1 on error with errno set.
int stat(const char *name, struct stat *buf)
{
FILINFO info;
int res;
time_t epoch;
uint16_t mode;
errno = 0;
// f_stat does not handle / or . as root directory
if (strcmp(name,"/") == 0 || strcmp(name,".") == 0)
{
buf->st_atime = 0;
buf->st_mtime = 0;
buf->st_ctime = 0;
buf->st_uid= 0;
buf->st_gid= 0;
buf->st_size = 0;
buf->st_mode = S_IFDIR;
return(0);
}
res = f_stat(name, &info);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
buf->st_size = info.fsize;
epoch = fat_time_to_unix(info.fdate, info.ftime);
buf->st_atime = epoch; // Access time
buf->st_mtime = epoch; // Modification time
buf->st_ctime = epoch; // Creation time
// We only handle read only case
mode = (FATFS_R | FATFS_X);
if( !(info.fattrib & AM_RDO))
mode |= (FATFS_W); // enable write if NOT read only
if(info.fattrib & AM_SYS)
{
buf->st_uid= 0;
buf->st_gid= 0;
}
{
buf->st_uid=1000;
buf->st_gid=1000;
}
if(info.fattrib & AM_DIR)
mode |= S_IFDIR;
else
mode |= S_IFREG;
buf->st_mode = mode;
return(0);
}
///@brief Set Modification and Access time of a file
///@param[in] filename: file name
///@param[in *times: access and modication utimbuf structure, if NULL use current time
///@return 0 if ok, -1 on error
int utime(const char *filename, const struct utimbuf *times)
{
FILINFO fno;
uint16_t fdate,ftime;
time_t ut = 0;
int res;
if(times)
ut = times->modtime;
unix_time_to_fat(ut, (uint16_t *) &fdate, (uint16_t *) &ftime);
fno.fdate = fdate;
fno.ftime = ftime;
res = f_utime(filename, (FILINFO *) &fno);
return( fatfs_to_errno(res) );
}
int64_t fs_getfree() {
FATFS *fs;
DWORD fre_clust, fre_sect, tot_sect;
/* Get volume information and free clusters of drive 1 */
FRESULT res = f_getfree("/", &fre_clust, &fs);
if (res) return(res);
/* Get total sectors and free sectors */
fre_sect = fre_clust * fs->csize;
return (int64_t)(fre_sect)*512;
}
int64_t fs_gettotal() {
FATFS *fs;
DWORD fre_clust, fre_sect, tot_sect;
/* Get volume information and free clusters of drive 1 */
FRESULT res = f_getfree("/", &fre_clust, &fs);
if (res) return(res);
/* Get total sectors and free sectors */
tot_sect = (fs->n_fatent - 2) * fs->csize;
return (int64_t)(tot_sect)*512;
}
// =============================================
// =============================================
/// - POSIX file and directory manipulation
// =============================================
// =============================================
/// @brief POSIX Basename of filename.
///
/// - man page (3).
///
/// @param[in] str: string to find basename in.
///
/// @return pointer to basename of string.
char *basename(const char *str)
{
const char *base = str;
if(!str)
return("");
while(*str)
{
if(*str++ == '/')
base = str;
}
return (char*)base;
}
/// @brief File extention of a file name.
/// NOT POSIX
///
/// @param[in] str: string to find extension in.
///
/// @return pointer to basename extension.
char *baseext(char *str)
{
char *ext = "";
while(*str)
{
if(*str++ == '.')
ext = str;
}
return(ext);
}
/// @brief POSIX change directory.
///
/// - man page chdir (2).
///
/// @param[in] pathname: directory to change to
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int chdir(const char *pathname)
{
errno = 0;
int res = f_chdir(pathname);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
/// @brief POSIX chmod function - change file access permission
/// Unfortunately file f_open modes and f_chmod modes are not the same
/// Files that are open have way more options - but only while the file is open.
/// - so this is a bit of a hack - we can only set read only - if on one has write perms
///
/// - man page chmod (2).
///
/// @param[in] pathname: filename string.
/// @param[in] mode: POSIX chmod modes.
///
/// @return fileno on success.
int chmod(const char *pathname, mode_t mode)
{
int rc;
errno = 0;
// FIXME for now we combine user,group and other perms and ask if anyone has write perms ?
// Read only ???
if ( !( mode & ( S_IWUSR | S_IWGRP | S_IWOTH)))
{
// file is read only
rc = f_chmod(pathname, AM_RDO, AM_RDO);
if (rc != FR_OK)
{
errno = fatfs_to_errno(rc);
return(-1);
}
}
return(0);
}
/// @brief POSIX directory name of a filename.
/// Return the index of the last '/' character.
///
/// - Example:
/// @code
/// dir[0] = 0;
/// ret = dirname(path)
/// if(ret)
/// strncpy(dir,path,ret);
/// @endcode
///
/// @param[in] str: string to examine.
/// @return 0 if no directory part.
/// @return index of last '/' character.
///
int dirname(char *str)
{
int end = 0;
int ind = 0;
if(!str)
return(0);
while(*str)
{
if(*str == '/')
end = ind;
++str;
++ind;
}
return(end);
}
#if 0
/// @brief POSIX fchmod function - change file access permission
/// FatFS does not have a function that will map to this
///
/// - man page fchmod (2).
///
/// @param[in] fd: file handle
/// @param[in] mode: POSIX chmod modes.
///
/// @return fileno on success.
int fchmod(int fd, mode_t mode)
{
//FIXME TODO
return (-1);
}
#endif
/// @brief POSIX get current working directory
///
/// - man page getcwd (2).
///
/// @param[in] pathname: directory to change to
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
char *getcwd(char *pathname, int len)
{
int res;
errno = 0;
res = f_getcwd(pathname, len);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(NULL);
}
return(pathname);
}
/// @brief POSIX make a directory.
///
/// - man page mkdir (2).
///
/// @param[in] pathname: directory to create
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int mkdir(const char *pathname, mode_t mode)
{
errno = 0;
if(mode)
{
if(chmod(pathname, mode))
return(-1);
}
int res = f_mkdir(pathname);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
/// @brief POSIX rename a file by name.
///
/// - man page (2).
///
/// @param[in] oldpath: original name.
/// @param[in] newpath: new name.
///
/// @return 0 on success.
/// @return -1 on error with errno set.
int rename(const char *oldpath, const char *newpath)
{
/* Rename an object */
int rc;
errno = 0;
rc = f_rename(oldpath, newpath);
if(rc)
{
errno = fatfs_to_errno(rc);
return(-1);
}
return(0);
}
/// @brief POSIX delete a directory.
///
/// - man page rmdir (2).
///
/// @param[in] pathname: directory to delete.
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int rmdir(const char *pathname)
{
errno = 0;
int res = f_unlink(pathname);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
/// @brief POSIX delete a file.
///
/// - man page unlink (2).
///
/// @param[in] pathname: filename to delete.
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int unlink(const char *pathname)
{
errno = 0;
int res = f_unlink(pathname);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
// =============================================
// =============================================
/// - POSIX - directory scanning functions
// =============================================
// =============================================
/// @brief POSIX closedir
/// - man page closedir (2).
///
/// @param[in] dirp: DIR * directory handle
///
/// @return 0 on sucess.
/// @return -1 on error with errno set.
int closedir(DIR *dirp)
{
int res = f_closedir (dirp);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(-1);
}
return(0);
}
/// @brief POSIX opendir
/// - man page opendir (2).
///
/// @param[in] pathname: directory to delete.
///
/// @return DIR * on sucess.
/// @return NULL on error with errno set.
static DIR _dp;
DIR *opendir(const char *pathdir)
{
int res = f_opendir((DIR *) &_dp, pathdir);
if(res != FR_OK)
{
errno = fatfs_to_errno(res);
return(NULL);
}
return ((DIR *) &_dp);
}
/// @brief POSIX opendir
/// - man page readdir(2).
///
/// @param[in] dirp: DIR * directory handle
///
/// @return DIR * on sucess.
/// @return NULL on error with errno set.
static dirent_t _de;
dirent_t * readdir(DIR *dirp)
{
FILINFO fno;
int len;
int res;
_de.d_name[0] = 0;
res = f_readdir ( dirp, &fno );
if(res != FR_OK || fno.fname[0] == 0)
{
errno = fatfs_to_errno(res);
return(NULL);
}
len = strlen(fno.fname);
strncpy(_de.d_name,fno.fname,len);
_de.d_name[len] = 0;
return( (dirent_t *) &_de);
}
// =============================================
// =============================================
/// - POSIX error functions
// =============================================
// =============================================
/// @brief clrerror resets stream EOF and error flags
/// - man page clrerror(3).
///
/// @param[in] stream: POSIX stream pointer.
/// @return EOF on error with errno set.
void clrerror(FILE *stream)
{
stream->flags &= ~__SEOF;
stream->flags &= ~__SERR;
}
/// @brief ferror reports if the stream has an error flag set
/// - man page ferror (3).
///
/// @param[in] stream: POSIX stream pointer.
/// @return 1 if EOF set, 0 otherwise.
int ferror(FILE *stream)
{
if(stream->flags & __SERR)
return(1);
return(0);
}
/// @brief POSIX perror() - convert POSIX errno to text with user message.
///
/// - man page errno (3).
///
/// @param[in] s: User message displayed before the error message
///
/// @see sys_errlist[].
/// @return void.
void perror(const char *s)
{
const char *ptr = NULL;
if(errno >=0 && errno < EBADMSG)
ptr = sys_errlist[errno];
else
ptr = sys_errlist[EBADMSG];
if(s && *s)
printf("%s: %s\n", s, ptr);
else
printf("%s\n", ptr);
}
/// @brief POSIX strerror() - convert POSIX errno to text with user message.
///
/// - man page strerror (3).
///
/// @param[in] errnum: index for sys_errlist[]
///
/// @see sys_errlist[].
/// @return char *
char *strerror(int errnum)
{
return( (char *)sys_errlist[errnum] );
}
/// @brief POSIX strerror_r() - convert POSIX errno to text with user message.
///
/// - man page strerror (3).
///
/// @param[in] errnum: index for sys_errlist[]
/// @param[in] buf: user buffer for error message
/// @param[in] buflen: length of user buffer for error message
///
/// @see sys_errlist[].
/// @return char *
char *__wrap_strerror_r(int errnum, char *buf, size_t buflen)
{
strncpy(buf, sys_errlist[errnum], buflen);
return(buf);
}
// =============================================
// =============================================
/// Device open functions
// =============================================
// =============================================
/// @brief Assign stdin,stdout,stderr or any use defined I/O
/// NOT POSIX
///
/// @param[in] *put: uart putc function pointer
/// @param[in] *get: uart gutc function pointer
FILE *
fdevopen(int (*put)(char, FILE *), int (*get)(FILE *))
{
FILE *s;
if (put == 0 && get == 0)
return 0;
if ((s = calloc(1, sizeof(FILE))) == 0)
return 0;
s->flags = __SMALLOC;
if (get != 0) {
s->get = get;
s->flags |= __SRD;
// We assign the first device with a read discriptor to stdin
// Only assign once
if (stdin == 0)
stdin = s;
}
if (put != 0) {
s->put = put;
s->flags |= __SWR;
// NOTE: We assign the first device with a write to both STDOUT andd STDERR
// Only assign in unassigned
if (stdout == 0)
stdout = s;
if (stderr == 0)
stderr = s;
}
return s;
}
// =============================================
// =============================================
/// - FatFS to POSIX bridge functions
// =============================================
// =============================================
/// @brief Formt SD card
/// @param[in] *name: device name
/// @retrun void
/*
int mkfs(char *name)
{
FATFS fs;
uint8_t *mem;
int res;
int len;
int c;
char dev[4];
len = MATCH(name,"/dev/sd");
if(!len)
{
printf("Expected /dev/sda .. /dev/sdj\n");
return(0);
}
// Convert /dev/sd[a-j] to 0: .. 9:
dev[1] = ':';
dev[2] = 0;
c = tolower( name[len-1] );
if(c >= 'a' && c <= ('a' + 9))
dev[0] = (c - 'a');
dev[3] = 0;
// Register work area to the logical drive 0:
res = f_mount(&fs, dev, 0);
if(!res)
{
put_rc(res);
return(0);
}
// Allocate memory for mkfs function
mem = malloc(1024);
if(!mem)
return(0);
// Create FAT volume on the logical drive 0
// 2nd argument is ignored.
res = f_mkfs(dev, FM_FAT32, 0, mem, 1024);
if(res)
{
put_rc(res);
free(mem);
return(0);
}
free(mem);
return(1);
}
*/
/// @brief Private FatFs function called by fgetc() to get a byte from file stream
/// FIXME buffer this function call
/// NOT POSIX
/// open() assigns stream->get = fatfs_getc()
///
/// - man page fgetc (3).
/// - Notes: fgetc does all tests prior to caling us, including ungetc.
///
/// @param[in] stream: POSIX stream pointer.
///
/// @return character.
/// @return EOF on error with errno set.
int fatfs_getc(FILE *stream)
{
FIL *fh;
UINT size;
int res;
uint8_t c;
long pos;
errno = 0;
if(stream == NULL)
{
errno = EBADF; // Bad File Number
return(EOF);
}
fh = (FIL *) fdev_get_udata(stream);
if(fh == NULL)
{
errno = EBADF; // Bad File Number
return(EOF);
}
res = f_read(fh, &c, 1, (UINT *) &size);
if( res != FR_OK || size != 1)
{
errno = fatfs_to_errno(res);
stream->flags |= __SEOF;
return(EOF);
}
// AUTOMATIC end of line METHOD detection
// ALWAYS return '\n' for ALL methods
// History: End of line (EOL) characters sometimes differ, mostly legacy systems, and modern UNIX (which uses just '\n')
// '\r' ONLY
// '\r\n'
// '\n'
// The difference was mostly from the way old mechanical printers were controlled.
// '\n' (New Line = NL) advanced the line
// '\r' (Charage Return = CR) moved the print head to start of line
// '\t' (Tabstop = TAB)
// '\f' (Form feed = FF)
// The problem with mechanical devices is that each had differing control and time delays to deal with.
// (TAB, CR, NL and FF) did different things and took differing times depending on the device.
//
// Long before DOS UNIX took the position that controlling physical devices must be a device drivers problem only.
// They reasoned if users had to worry about all the ugly controll and timing issues no code would be portable.
// Therefore they made NL just a SYMBOL for the driver to determine what to do.
// This design philosophy argued if you needed better control its better to use a real designed purposed tool for it.
// (ie. like curses or termcap).
// Here to deal with those other old ugly stupid pointless EOL methods we convert to just a symbol '\n'
// FROM '\n' OR '\r'char OR '\r\n' TO '\n'
// Note: char != '\n'
if(c == '\r')
{
// PEEK forward 1 character
pos = f_tell(fh);
// Check for trailing '\n' or EOF
res = f_read(fh, &c, 1, (UINT *) &size);
if(res != FR_OK || size != 1)
{
// '\r' with EOF impiles '\n'
return('\n');
}
// This file must be '\r' ONLY for end of line
if(c != '\n')
{
// Not '\n' or EOF o move file pointer back to just after the '\r'
f_lseek(fh, pos);
return('\n');
}
c = '\n';
}
return(c & 0xff);
}
/// @brief Private FatFs function called by fputc() to put a byte from file stream
/// NOT POSIX
/// open() assigns stream->put = fatfs_putc()
///
/// - man page fputc (3).
/// - Notes: fputc does all tests prior to caling us.
///
/// @param[in] c: character.
/// @param[in] stream: POSIX stream pointer.
///
/// @return character
/// @return EOF on error with errno set.
int fatfs_putc(char c, FILE *stream)
{
int res;
FIL *fh;
UINT size;
errno = 0;
if(stream == NULL)
{
errno = EBADF; // Bad File Number
return(EOF);
}
fh = (FIL *) fdev_get_udata(stream);
if(fh == NULL)
{
errno = EBADF; // Bad File Number
return(EOF);
}
res = f_write(fh, &c, 1, (UINT *) &size);
if( res != FR_OK || size != 1)
{
errno = fatfs_to_errno(res);
stream->flags |= __SEOF;
return(EOF);
}
return(c);
}
/// @brief Convert FafFs error result to POSIX errno.
/// NOT POSIX
///
/// - man page errno (3).
///
/// @param[in] Result: FatFs Result code.
///
/// @return POSIX errno.
/// @return EBADMSG if no conversion possible.
int fatfs_to_errno( FRESULT Result )
{
switch( Result )
{
case FR_OK: /* FatFS (0) Succeeded */
return (0); /* POSIX OK */
case FR_DISK_ERR: /* FatFS (1) A hard error occurred in the low level disk I/O layer */
return (EIO); /* POSIX Input/output error (POSIX.1) */
case FR_INT_ERR: /* FatFS (2) Assertion failed */
return (EPERM); /* POSIX Operation not permitted (POSIX.1) */
case FR_NOT_READY: /* FatFS (3) The physical drive cannot work */
return (EBUSY); /* POSIX Device or resource busy (POSIX.1) */
case FR_NO_FILE: /* FatFS (4) Could not find the file */
return (ENOENT); /* POSIX No such file or directory (POSIX.1) */
case FR_NO_PATH: /* FatFS (5) Could not find the path */
return (ENOENT); /* POSIX No such file or directory (POSIX.1) */
case FR_INVALID_NAME: /* FatFS (6) The path name format is invalid */
return (EINVAL); /* POSIX Invalid argument (POSIX.1) */
case FR_DENIED: /* FatFS (7) Access denied due to prohibited access or directory full */
return (EACCES); /* POSIX Permission denied (POSIX.1) */
case FR_EXIST: /* FatFS (8) Access denied due to prohibited access */
return (EACCES); /* POSIX Permission denied (POSIX.1) */
case FR_INVALID_OBJECT: /* FatFS (9) The file/directory object is invalid */
return (EINVAL); /* POSIX Invalid argument (POSIX.1) */
case FR_WRITE_PROTECTED: /* FatFS (10) The physical drive is write protected */
return(EROFS); /* POSIX Read-only filesystem (POSIX.1) */
case FR_INVALID_DRIVE: /* FatFS (11) The logical drive number is invalid */
return(ENXIO); /* POSIX No such device or address (POSIX.1) */
case FR_NOT_ENABLED: /* FatFS (12) The volume has no work area */
return (ENOSPC); /* POSIX No space left on device (POSIX.1) */
case FR_NO_FILESYSTEM: /* FatFS (13) There is no valid FAT volume */
return(ENXIO); /* POSIX No such device or address (POSIX.1) */
case FR_MKFS_ABORTED: /* FatFS (14) The f_mkfs() aborted due to any parameter error */
return (EINVAL); /* POSIX Invalid argument (POSIX.1) */
case FR_TIMEOUT: /* FatFS (15) Could not get a grant to access the volume within defined period */
return (EBUSY); /* POSIX Device or resource busy (POSIX.1) */
case FR_LOCKED: /* FatFS (16) The operation is rejected according to the file sharing policy */
return (EBUSY); /* POSIX Device or resource busy (POSIX.1) */
case FR_NOT_ENOUGH_CORE: /* FatFS (17) LFN working buffer could not be allocated */
return (ENOMEM); /* POSIX Not enough space (POSIX.1) */
case FR_TOO_MANY_OPEN_FILES:/* FatFS (18) Number of open files > _FS_SHARE */
return (EMFILE); /* POSIX Too many open files (POSIX.1) */
case FR_INVALID_PARAMETER:/* FatFS (19) Given parameter is invalid */
return (EINVAL); /* POSIX Invalid argument (POSIX.1) */
}
return (EBADMSG); /* POSIX Bad message (POSIX.1) */
}
/// @brief Convert FatFS file handle to POSIX fileno.
/// NOT POSIX
///
/// @param[in] fh: FatFS file pointer.
///
/// @return fileno on success.
/// @return -1 on error with errno set to EBADF.
int fatfs_to_fileno(FIL *fh)
{
int i;
FILE *stream;
if(fh == NULL)
{
errno = EBADF;
return(-1);
}
for(i=0;itm_year < 70) {
return((time_t)-1);
}
n = t->tm_year + 1900 - 1;
epoch = (t->tm_year - 70) * YEAR +
((n / 4 - n / 100 + n / 400) - (1969 / 4 - 1969 / 100 + 1969 / 400)) * DAY;
y = t->tm_year + 1900;
m = 0;
for(i = 0; i < t->tm_mon; i++) {
epoch += mon [m] * DAY;
if(m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
epoch += DAY;
if(++m > 11) {
m = 0;
y++;
}
}
epoch += (t->tm_mday - 1) * DAY;
epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec;
return epoch;
}
/// @brief Convert FatFs file date and time to POSIX epoch seconds.
/// NOT POSIX
///
/// - man page timegm (3).
///
/// @param[in] date: FatFs date.
/// @param[in] time: FatFs time.
///
/// @see timegm()
///
/// @return epoch seconds
time_t fat_time_to_unix(uint16_t date, uint16_t time)
{
struct tm tp;
time_t unix;
memset(&tp, 0, sizeof(struct tm));
tp.tm_sec = (time << 1) & 0x3e; // 2 second resolution
tp.tm_min = ((time >> 5) & 0x3f);
tp.tm_hour = ((time >> 11) & 0x1f);
tp.tm_mday = (date & 0x1f);
tp.tm_mon = ((date >> 5) & 0x0f) - 1;
tp.tm_year = ((date >> 9) & 0x7f) + 80;
unix = replace_mktime( &tp );
return( unix );
}
/// @brief Convert Linux POSIX time_t to FAT32 date and time.
/// NOT POSIX
/// - man page gmtime (3).
/// @param[in] epoch: unix epoch seconds
/// @param[in] *date: fat32 date
/// @param[in] *time: fat32 time
/// @return void
void unix_time_to_fat(time_t epoch, uint16_t *date, uint16_t *time)
{
struct tm *t = gmtime((time_t *) &epoch);
/* Pack date and time into a uint32_t variable */
*date = ((uint16_t)(t->tm_year - 80) << 9)
| (((uint16_t)t->tm_mon+1) << 5)
| (((uint16_t)t->tm_mday));
*time = ((uint16_t)t->tm_hour << 11)
| ((uint16_t)t->tm_min << 5)
| ((uint16_t)t->tm_sec >> 1);
}
/// @brief Convert POSIX fileno to FatFS handle
/// NOT POSIX
///
/// - FatFS file handle is pointed to by the avr-libc stream->udata.
///
/// @param[in] fileno: fileno of file
///
/// @return FIL * FatFS file handle on success.
/// @return NULL if POSIX fileno is invalid NULL
FIL *fileno_to_fatfs(int fileno)
{
FILE *stream;
FIL *fh;
if(isatty( fileno ))
{
errno = EBADF;
return(NULL);
}
// checks if fileno out of bounds
stream = fileno_to_stream(fileno);
if( stream == NULL )
return(NULL);
fh = fdev_get_udata(stream);
if(fh == NULL)
{
errno = EBADF;
return(NULL);
}
return(fh);
}
/// @brief Free POSIX fileno FILE descriptor.
/// NOT POSIX
///
/// @param[in] fileno: POSIX file number __iob[] index.
///
/// @return fileno on success.
/// @return -1 on failure.
int free_file_descriptor(int fileno)
{
FILE *stream;
FIL *fh;
if(isatty( fileno ))
{
errno = EBADF;
return(-1);
}
// checks if fileno out of bounds
stream = fileno_to_stream(fileno);
if(stream == NULL)
{
return(-1);
}
fh = fdev_get_udata(stream);
if(fh != NULL)
{
free(fh);
}
if(stream->buf != NULL && stream->flags & __SMALLOC)
{
free(stream->buf);
}
__iob[fileno] = NULL;
free(stream);
return(fileno);
}
// =============================================
/// @brief Allocate a POSIX FILE descriptor.
/// NOT POSIX
///
/// @return fileno on success.
/// @return -1 on failure with errno set.
int new_file_descriptor( void )
{
int i;
FILE *stream;
FIL *fh;
for(i=0;isent++;
fputc(ch, (FILE *) p->buffer);
}
*/
/// @brief fprintf function
/// Example user defined printf function using fputc for I/O
/// This method allows I/O to devices and strings without typical C++ overhead
/// @param[in] *fp: FILE stream pointer
/// @param[in] fmt: printf forat string
/// @param[in] ...: vararg list or arguments
/// @return size of printed result
int
fprintf(FILE *fp, const char *fmt, ...)
{
va_list va;
char* buf;
int16_t len, i;
va_start(va, fmt);
len = vasprintf(&buf, fmt, va);
if (len > 0) {
for(i = 0; i < len; i++) {
fputc(buf[i], fp);
}
} else {
return -1;
}
va_end(va);
return len;
}