mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Implemented large parts of the stdio(3), including fprintf.
Made FILE an interface to various backends. This allows application writers to override the standard FILE API functions with their own backends. This is highly unportable - it'd be nice if a real standard existed for this. glibc already does something like this internally, but AFAIK you can't hook into it. Added fdopen(3), fopen(3), fregister(3), funregister(3), fread(3), fwrite(3), fseek(3), clearerr(3), ferror(3), feof(3), rewind(3), ftell(3), fflush(3), fclose(3), fileno(3), fnewline(3), fcloseall(3), memset(3), stdio(3), vfprintf(3), fprintf(3), and vprintf(3). Added a file-descriptor backend to the FILE API. fd's {0, 1, 2} are now initialized as stdin, stdout, and stderr when the standard library initializes. fcloseall(3) is now called on exit(3). decl/intn_t_.h now @include(size_t.h) instead of declaring it itself. Added <stdint.h>. The following programs now flush stdout: cat(1), clear(1), editor(1), init(1), mxsh(1). printf(3) is now hooked up against vprintf(3), while Maxsi::PrintF remains using the system call, for now.
This commit is contained in:
parent
4841d83ff8
commit
fdbd4ca90d
18 changed files with 560 additions and 281 deletions
|
@ -28,7 +28,11 @@ LDFLAGS=$(CPULDFLAGS)
|
|||
ASFLAGS=$(CPUASFLAGS)
|
||||
NASMFLAGS=$(CPUNASMFLAGS)
|
||||
|
||||
COBJS=c/file.o
|
||||
COBJS=\
|
||||
c/file.o \
|
||||
c/fdio.o \
|
||||
c/stdio.o \
|
||||
|
||||
CHEADERS=\
|
||||
c/h/unistd.h \
|
||||
c/h/stdlib.h \
|
||||
|
@ -47,6 +51,7 @@ c/h/sys/types.h \
|
|||
c/h/sys/wait.h \
|
||||
c/h/stdio.h \
|
||||
c/h/signal.h \
|
||||
c/h/stdint.h \
|
||||
|
||||
COMMONOBJS=c++.o memory.o heap.o string.o error.o format.o
|
||||
SORTIXOBJS:=$(addprefix sortix/,$(COMMONOBJS))
|
||||
|
|
|
@ -1,5 +1,32 @@
|
|||
#ifndef _FILE_DECL
|
||||
#define _FILE_DECL
|
||||
struct _IO_FILE;
|
||||
typedef struct _IO_FILE FILE;
|
||||
#define BUFSIZ 8192UL
|
||||
#define _FILE_REGISTERED (1<<0)
|
||||
#define _FILE_NO_BUFFER (1<<1)
|
||||
typedef struct _FILE
|
||||
{
|
||||
/* This is non-standard, but useful. If you allocate your own FILE and
|
||||
register it with fregister, feel free to use modify the following members
|
||||
to customize how it works. Do not call or use these data structures,
|
||||
though, as the standard library library may do various kinds of buffering
|
||||
and locale/encoding conversion. */
|
||||
size_t buffersize;
|
||||
char* buffer;
|
||||
void* user;
|
||||
size_t (*read_func)(void* ptr, size_t size, size_t nmemb, void* user);
|
||||
size_t (*write_func)(const void* ptr, size_t size, size_t nmemb, void* user);
|
||||
int (*seek_func)(void* user, long offset, int whence);
|
||||
long (*tell_func)(void* user);
|
||||
void (*clearerr_func)(void* user);
|
||||
int (*eof_func)(void* user);
|
||||
int (*error_func)(void* user);
|
||||
int (*fileno_func)(void* user);
|
||||
int (*close_func)(void* user);
|
||||
void (*free_func)(struct _FILE* fp);
|
||||
/* Application writers shouldn't use anything beyond this point. */
|
||||
struct _FILE* prev;
|
||||
struct _FILE* next;
|
||||
int flags;
|
||||
size_t bufferused;
|
||||
} FILE;
|
||||
#endif
|
||||
|
|
197
libmaxsi/c/fdio.c
Normal file
197
libmaxsi/c/fdio.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
LibMaxsi is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
LibMaxsi 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 Lesser General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
fdio.c
|
||||
Handles the file descriptor backend for the FILE* API.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
const int FDIO_WRITING = (1<<0);
|
||||
const int FDIO_READING = (1<<1);
|
||||
const int FDIO_APPEND = (1<<2);
|
||||
const int FDIO_ERROR = (1<<3);
|
||||
const int FDIO_EOF = (1<<4);
|
||||
|
||||
typedef struct fdio_struct
|
||||
{
|
||||
int flags;
|
||||
int fd;
|
||||
} fdio_t;
|
||||
|
||||
static size_t fdio_read(void* ptr, size_t size, size_t nmemb, void* user)
|
||||
{
|
||||
uint8_t* buf = (uint8_t*) ptr;
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
if ( !(fdio->flags & FDIO_READING) ) { errno = EBADF; return 0; }
|
||||
size_t sofar = 0;
|
||||
size_t total = size * nmemb;
|
||||
while ( sofar < total )
|
||||
{
|
||||
ssize_t numbytes = read(fdio->fd, buf + sofar, total - sofar);
|
||||
if ( numbytes < 0 ) { fdio->flags |= FDIO_ERROR; return sofar; }
|
||||
if ( numbytes == 0 ) { fdio->flags |= FDIO_EOF; return sofar; }
|
||||
sofar += numbytes;
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
static size_t fdio_write(const void* ptr, size_t size, size_t nmemb, void* user)
|
||||
{
|
||||
const uint8_t* buf = (const uint8_t*) ptr;
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
if ( !(fdio->flags & FDIO_WRITING) ) { errno = EBADF; return 0; }
|
||||
size_t sofar = 0;
|
||||
size_t total = size * nmemb;
|
||||
while ( sofar < total )
|
||||
{
|
||||
ssize_t numbytes = write(fdio->fd, buf + sofar, total - sofar);
|
||||
if ( numbytes < 0 ) { fdio->flags |= FDIO_ERROR; return sofar; }
|
||||
sofar += numbytes;
|
||||
}
|
||||
return sofar;
|
||||
}
|
||||
|
||||
static int fdio_seek(void* user, long offset, int whence)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
// TODO: lseek(2) is not implemented yet!
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static long fdio_tell(void* user)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
// TODO: lseek(2) is not implemented yet!
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fdio_clearerr(void* user)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
fdio->flags &= ~FDIO_ERROR;
|
||||
}
|
||||
|
||||
static int fdio_eof(void* user)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
return fdio->flags & FDIO_EOF;
|
||||
}
|
||||
|
||||
static int fdio_error(void* user)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
return fdio->flags & FDIO_ERROR;
|
||||
}
|
||||
|
||||
static int fdio_fileno(void* user)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
return fdio->fd;
|
||||
}
|
||||
|
||||
static int fdio_close(void* user)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) user;
|
||||
int result = close(fdio->fd);
|
||||
free(fdio);
|
||||
return result;
|
||||
}
|
||||
|
||||
int fdio_install(FILE* fp, const char* mode, int fd)
|
||||
{
|
||||
fdio_t* fdio = (fdio_t*) calloc(1, sizeof(fdio_t));
|
||||
if ( !fdio ) { return 0; }
|
||||
fdio->fd = fd;
|
||||
char c;
|
||||
// TODO: This is too hacky and a little buggy.
|
||||
while ( ( c = *mode++ ) )
|
||||
{
|
||||
switch ( c )
|
||||
{
|
||||
case 'r': fdio->flags |= FDIO_READING; break;
|
||||
case 'w': fdio->flags |= FDIO_WRITING; break;
|
||||
case '+': fdio->flags |= FDIO_READING | FDIO_WRITING; break;
|
||||
case 'a': fdio->flags |= FDIO_WRITING | FDIO_APPEND; break;
|
||||
case 'b': break;
|
||||
default: errno = EINVAL; free(fdio); return 0;
|
||||
}
|
||||
}
|
||||
fp->user = fdio;
|
||||
fp->read_func = fdio_read;
|
||||
fp->write_func = fdio_write;
|
||||
fp->seek_func = fdio_seek;
|
||||
fp->tell_func = fdio_tell;
|
||||
fp->clearerr_func = fdio_clearerr;
|
||||
fp->eof_func = fdio_eof;
|
||||
fp->error_func = fdio_error;
|
||||
fp->fileno_func = fdio_fileno;
|
||||
fp->close_func = fdio_close;
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* fdio_newfile(int fd, const char* mode)
|
||||
{
|
||||
FILE* fp = fnewfile();
|
||||
if ( !fp ) { return NULL; }
|
||||
if ( !fdio_install(fp, mode, fd) ) { fclose(fp); return NULL; }
|
||||
return fp;
|
||||
}
|
||||
|
||||
FILE* fdopen(int fd, const char* mode)
|
||||
{
|
||||
return fdio_newfile(fd, mode);
|
||||
}
|
||||
|
||||
FILE* fopen(const char* path, const char* mode)
|
||||
{
|
||||
int omode = 0;
|
||||
int oflags = 0;
|
||||
char c;
|
||||
// TODO: This is too hacky and a little buggy.
|
||||
while ( ( c = *mode++ ) )
|
||||
{
|
||||
switch ( c )
|
||||
{
|
||||
case 'r': omode = O_RDONLY; break;
|
||||
case 'a': oflags |= O_APPEND; /* fall-through */
|
||||
case 'w': omode = O_WRONLY; break;
|
||||
case '+':
|
||||
if ( omode == O_WRONLY ) { oflags |= O_CREAT | O_TRUNC; }
|
||||
omode = O_RDWR;
|
||||
break;
|
||||
case 'b': break;
|
||||
default: errno = EINVAL; return 0;
|
||||
}
|
||||
}
|
||||
int fd = open(path, fd, 0666);
|
||||
if ( fd < 0 ) { return NULL; }
|
||||
FILE* fp = fdopen(fd, mode);
|
||||
if ( !fp ) { close(fd); return NULL; }
|
||||
return fp;
|
||||
}
|
||||
|
38
libmaxsi/c/fdio.h
Normal file
38
libmaxsi/c/fdio.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
LibMaxsi is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
LibMaxsi 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 Lesser General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
fdio.h
|
||||
Handles the file descriptor backend for the FILE* API.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _FDIO_H
|
||||
#define _FDIO_H 1
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
int fdio_install(FILE* fp, const char* mode, int fd);
|
||||
FILE* fdio_newfile(int fd, const char* mode);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -18,288 +18,168 @@
|
|||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
file.c
|
||||
Implements every related to the FILE structure. This API is not compatible
|
||||
enough with LibMaxsi's design goals, so it is implemented as a layer upon
|
||||
the C functions in LibMaxsi.
|
||||
FILE* in libmaxsi is an interface to various implementations of the FILE*
|
||||
API. This allows stuff like fmemopen, but also allows the application
|
||||
programmers to provide their own backends.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if 0
|
||||
FILE* firstfile = NULL;
|
||||
|
||||
// TODO: Make a real errno system!
|
||||
volatile int errno;
|
||||
#define EINVAL 1
|
||||
|
||||
typedef struct
|
||||
void fregister(FILE* fp)
|
||||
{
|
||||
off_t pos;
|
||||
mbstate_t state;
|
||||
} _fpos_t;
|
||||
|
||||
struct _IO_FILE
|
||||
{
|
||||
int fd;
|
||||
_fpos_t pos;
|
||||
};
|
||||
|
||||
// TODO: Actually implement these stubs.
|
||||
|
||||
char* fgets(char* restrict s, int n, FILE* restrict stream)
|
||||
{
|
||||
return NULL;
|
||||
fp->flags |= _FILE_REGISTERED;
|
||||
if ( !firstfile ) { firstfile = fp; return; }
|
||||
fp->next = firstfile;
|
||||
firstfile->prev = fp;
|
||||
firstfile = fp;
|
||||
}
|
||||
|
||||
FILE* fdopen(int fildes, const char* mode)
|
||||
void funregister(FILE* fp)
|
||||
{
|
||||
return NULL;
|
||||
if ( !(fp->flags & _FILE_REGISTERED) ) { return; }
|
||||
if ( !fp->prev ) { firstfile = fp->next; }
|
||||
if ( fp->prev ) { fp->prev->next = fp->next; }
|
||||
if ( fp->next ) { fp->next->prev = fp->prev; }
|
||||
fp->flags &= ~_FILE_REGISTERED;
|
||||
}
|
||||
|
||||
FILE *fmemopen(void* restrict buf, size_t size, const char* restrict mode)
|
||||
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* fp)
|
||||
{
|
||||
return NULL;
|
||||
if ( !fp->read_func ) { errno = EBADF; return 0; }
|
||||
return fp->read_func(ptr, size, nmemb, fp->user);
|
||||
}
|
||||
|
||||
FILE* fopen(const char* restrict filename, const char* restrict mode)
|
||||
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* fp)
|
||||
{
|
||||
int len;
|
||||
if ( mode[0] == '\0' ) { errno = EINVAL; return NULL; } else
|
||||
if ( mode[1] == '\1' ) { len = 1; } else
|
||||
if ( mode[2] == '\0' ) { len = 2; }
|
||||
if ( mode[3] == '\0' ) { len = 3; } else { errno = EINVAL; return NULL; }
|
||||
|
||||
int oflags;
|
||||
|
||||
if ( len == 1 || (len == 2 && mode[1] == 'b') )
|
||||
if ( !fp->write_func ) { errno = EBADF; return 0; }
|
||||
char* str = (char*) ptr;
|
||||
size_t total = size * nmemb;
|
||||
size_t sofar = 0;
|
||||
while ( sofar < total )
|
||||
{
|
||||
switch ( mode[0] )
|
||||
size_t left = total - sofar;
|
||||
if ( (!fp->bufferused && fp->buffersize <= left) || (fp->flags & _FILE_NO_BUFFER) )
|
||||
{
|
||||
case 'r': oflags = O_RDONLY; break;
|
||||
case 'w': oflags = O_WRONLY | O_TRUNC | O_CREAT; break;
|
||||
case 'a': oflags = O_WRONLY | O_APPEND | O_CREAT; break;
|
||||
default: errno = EINVAL; return NULL;
|
||||
return sofar + fp->write_func(str + sofar, 1, left, fp->user);
|
||||
}
|
||||
|
||||
size_t available = fp->buffersize - fp->bufferused;
|
||||
size_t count = ( left < available ) ? left : available;
|
||||
count = left;
|
||||
for ( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
char c = str[sofar++];
|
||||
fp->buffer[fp->bufferused++] = c;
|
||||
if ( c == '\n' )
|
||||
{
|
||||
if ( fflush(fp) ) { return sofar; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( fp->buffersize <= fp->bufferused )
|
||||
{
|
||||
if ( fflush(fp) ) { return sofar; }
|
||||
}
|
||||
}
|
||||
else if ( (len == 2 && mode[1] == '+') || (len == 3 && mode[1] == '+' && mode[1] == 'b') || (len == 3 && mode[1] == 'b' && mode[1] == '+') )
|
||||
return sofar;
|
||||
}
|
||||
|
||||
int fseek(FILE* fp, long offset, int whence)
|
||||
{
|
||||
return (fp->seek_func) ? fp->seek_func(fp->user, offset, whence) : 0;
|
||||
}
|
||||
|
||||
void clearerr(FILE* fp)
|
||||
{
|
||||
if ( fp->clearerr_func ) { fp->clearerr_func(fp->user); }
|
||||
}
|
||||
|
||||
int ferror(FILE* fp)
|
||||
{
|
||||
if ( !fp->error_func ) { return 0; }
|
||||
return fp->error_func(fp->user);
|
||||
}
|
||||
|
||||
int feof(FILE* fp)
|
||||
{
|
||||
if ( !fp->eof_func ) { return 0; }
|
||||
return fp->eof_func(fp->user);
|
||||
}
|
||||
|
||||
void rewind(FILE* fp)
|
||||
{
|
||||
fseek(fp, 0L, SEEK_SET);
|
||||
clearerr(fp);
|
||||
}
|
||||
|
||||
long ftell(FILE* fp)
|
||||
{
|
||||
if ( !fp->tell_func ) { errno = EBADF; return -1; }
|
||||
return fp->tell_func(fp->user);
|
||||
}
|
||||
|
||||
int fflush(FILE* fp)
|
||||
{
|
||||
if ( !fp )
|
||||
{
|
||||
switch ( mode[0] )
|
||||
{
|
||||
case 'r': oflags = O_RDWR; break;
|
||||
case 'w': oflags = O_RDWR | O_TRUNC | O_CREAT; break;
|
||||
case 'a': oflags = O_RDWR | O_APPEND | O_CREAT; break;
|
||||
default: errno = EINVAL; return NULL;
|
||||
}
|
||||
int result = 0;
|
||||
for ( fp = firstfile; fp; fp = fp->next ) { result |= fflush(fp); }
|
||||
return result;
|
||||
}
|
||||
else { errno = EINVAL; return NULL; }
|
||||
|
||||
// TODO: Does anything else modify this mask?
|
||||
// TODO: POSIX says this should be "S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH",
|
||||
// but Linux applies this in a simple test case!
|
||||
mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
FILE* file = malloc(sizeof(FILE));
|
||||
|
||||
if ( file == NULL ) { return NULL; }
|
||||
|
||||
int fd = open(filename, oflags, perms);
|
||||
|
||||
if ( fd < 0 ) { free(file); return NULL; }
|
||||
|
||||
file->fd = fd;
|
||||
// TODO: set other stuff here!
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
FILE* freopen(const char* restrict filename, const char *restrict mode, FILE* restrict stream)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE* popen(const char* command, const char* mode)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE* tmpfile(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fclose(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int feof(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ferror(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fflush(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fgetc(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fgetpos(FILE* restrict stream, fpos_t* restrict pos)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fileno(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fprintf(FILE* restrict stream, const char* restrict format, ...)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fputc(int c, FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fputs(const char* restrict s, FILE* restrict stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fscanf(FILE* restrict stream, const char* restrict format, ... )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fseek(FILE* stream, long offset, int whence)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fseeko(FILE* stream, off_t offset, int whence)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fsetpos(FILE* stream, const fpos_t* pos)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ftrylockfile(FILE* file)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getc(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getc_unlocked(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pclose(FILE* steam)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int putc(int c, FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int putc_unlocked(int c, FILE* steam)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int setvbuf(FILE* restrict stream, char* restrict buf, int type, size_t size)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ungetc(int c, FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int vfprintf(FILE* restrict stream, const char* restrict format, va_list ap)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int vfscanf(FILE* restrict stream, const char* restrict format, va_list arg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int vprintf(FILE* restrict stream, const char* restrict format, va_list ap)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
long ftell(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t ftello(FILE* stream)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t fread(void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream)
|
||||
{
|
||||
if ( !fp->write_func ) { errno = EBADF; return EOF; }
|
||||
if ( !fp->bufferused ) { return 0; }
|
||||
size_t written = fp->write_func(fp->buffer, 1, fp->bufferused, fp->user);
|
||||
if ( written < fp->bufferused ) { return EOF; }
|
||||
fp->bufferused = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t fwrite(const void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream)
|
||||
int fclose(FILE* fp)
|
||||
{
|
||||
return 0;
|
||||
int result = fflush(fp);
|
||||
result |= (fp->close_func) ? fp->close_func(fp->user) : 0;
|
||||
funregister(fp);
|
||||
if ( fp->free_func ) { fp->free_func(fp); }
|
||||
return result;
|
||||
}
|
||||
|
||||
void clearerr(FILE* stream)
|
||||
int fileno(FILE* fp)
|
||||
{
|
||||
|
||||
int result = (fp->fileno_func) ? fp->fileno_func(fp->user) : -1;
|
||||
if ( result < 0 ) { errno = EBADF; }
|
||||
return result;
|
||||
}
|
||||
|
||||
void flockfile(FILE* file)
|
||||
static void ffreefile(FILE* fp)
|
||||
{
|
||||
|
||||
free(fp->buffer);
|
||||
free(fp);
|
||||
}
|
||||
|
||||
void funlockfile(FILE* file)
|
||||
FILE* fnewfile(void)
|
||||
{
|
||||
|
||||
FILE* fp = (FILE*) calloc(sizeof(FILE), 1);
|
||||
if ( !fp ) { return NULL; }
|
||||
fp->buffersize = BUFSIZ;
|
||||
fp->buffer = (char*) malloc(fp->buffersize);
|
||||
if ( !fp->buffer ) { free(fp); return NULL; }
|
||||
fp->flags = 0;
|
||||
fp->free_func = ffreefile;
|
||||
fregister(fp);
|
||||
return fp;
|
||||
}
|
||||
|
||||
void rewind(FILE* stream)
|
||||
int fcloseall(void)
|
||||
{
|
||||
|
||||
int result = 0;
|
||||
while ( firstfile ) { result |= fclose(firstfile); }
|
||||
return (result) ? EOF : 0;
|
||||
}
|
||||
|
||||
void setbuf(FILE* restrict stream, char* restrict buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
39
libmaxsi/c/hsrc/stdint.h
Normal file
39
libmaxsi/c/hsrc/stdint.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
LibMaxsi is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
LibMaxsi 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 Lesser General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
stdint.h
|
||||
Integer types.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
/* TODO: POSIX-1.2008 compliance is only partial */
|
||||
|
||||
#ifndef _UNISTD_H
|
||||
#define _UNISTD_H 1
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
@include(intn_t.h)
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -29,18 +29,17 @@
|
|||
|
||||
__BEGIN_DECLS
|
||||
|
||||
@include(FILE.h)
|
||||
|
||||
struct _fpos_t;
|
||||
typedef struct _fpos_t fpos_t;
|
||||
|
||||
@include(off_t.h)
|
||||
@include(size_t.h)
|
||||
@include(ssize_t.h)
|
||||
@include(va_list.h)
|
||||
@include(NULL.h)
|
||||
|
||||
/* TODO: Implement BUFSIZ */
|
||||
@include(FILE.h)
|
||||
|
||||
struct _fpos_t;
|
||||
typedef struct _fpos_t fpos_t;
|
||||
|
||||
/* TODO: Implement L_ctermid */
|
||||
#if __POSIX_OBSOLETE <= 200801
|
||||
/* TODO: Implement L_tmpnam */
|
||||
|
@ -76,8 +75,22 @@ extern FILE* stderr;
|
|||
#define stdout stdout
|
||||
#define stderr stderr
|
||||
|
||||
extern void clearerr(FILE* stream);
|
||||
extern int fclose(FILE* stream);
|
||||
extern int feof(FILE* stream);
|
||||
extern int ferror(FILE* stream);
|
||||
extern int fflush(FILE* stream);
|
||||
extern int fileno(FILE* stream);
|
||||
extern int fprintf(FILE* restrict stream, const char* restrict format, ...);
|
||||
extern size_t fread(void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream);
|
||||
extern int fseek(FILE* stream, long offset, int whence);
|
||||
extern long ftell(FILE* stream);
|
||||
extern size_t fwrite(const void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream);
|
||||
extern void perror(const char* s);
|
||||
extern int printf(const char* restrict format, ...);
|
||||
extern void rewind(FILE* stream);
|
||||
extern int vfprintf(FILE* restrict stream, const char* restrict format, va_list ap);
|
||||
extern int vprintf(const char* restrict format, va_list ap);
|
||||
|
||||
/* TODO: These are not implemented in libmaxsi/sortix yet. */
|
||||
#ifndef SORTIX_UNIMPLEMENTED
|
||||
|
@ -91,18 +104,11 @@ extern FILE* open_memstream(char** bufp, size_t* sizep);
|
|||
extern FILE* popen(const char* command, const char* mode);
|
||||
extern FILE* tmpfile(void);
|
||||
extern int dprintf(int fildes, const char* restrict format, ...);
|
||||
extern int fclose(FILE* stream);
|
||||
extern int feof(FILE* stream);
|
||||
extern int ferror(FILE* stream);
|
||||
extern int fflush(FILE* stream);
|
||||
extern int fgetc(FILE* stream);
|
||||
extern int fgetpos(FILE* restrict stream, fpos_t* restrict pos);
|
||||
extern int fileno(FILE* stream);
|
||||
extern int fprintf(FILE* restrict stream, const char* restrict format, ...);
|
||||
extern int fputc(int c, FILE* stream);
|
||||
extern int fputs(const char* restrict s, FILE* restrict stream);
|
||||
extern int fscanf(FILE* restrict stream, const char* restrict format, ... );
|
||||
extern int fseek(FILE* stream, long offset, int whence);
|
||||
extern int fseeko(FILE* stream, off_t offset, int whence);
|
||||
extern int fsetpos(FILE* stream, const fpos_t* pos);
|
||||
extern int ftrylockfile(FILE* file);
|
||||
|
@ -126,23 +132,16 @@ extern int sprintf(char* restrict s, const char* restrict format, ...);
|
|||
extern int sscanf(const char* restrict s, const char* restrict format, ...);
|
||||
extern int ungetc(int c, FILE* stream);
|
||||
extern int vdprintf(int fildes, const char* restrict format, va_list ap);
|
||||
extern int vfprintf(FILE* restrict stream, const char* restrict format, va_list ap);
|
||||
extern int vfscanf(FILE* restrict stream, const char* restrict format, va_list arg);
|
||||
extern int vprintf(const char* restrict format, va_list ap);
|
||||
extern int vscanf(const char* restrict format, va_list arg);
|
||||
extern int vsnprintf(char* restrict, size_t, const char* restrict, va_list);
|
||||
extern int vsprintf(char* restrict s, const char* restrict format, va_list ap);
|
||||
extern int vsscanf(const char* restrict s, const char* restrict format, va_list arg);
|
||||
extern long ftell(FILE* stream);
|
||||
extern off_t ftello(FILE* stream);
|
||||
extern size_t fread(void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream);
|
||||
extern size_t fwrite(const void* restrict ptr, size_t size, size_t nitems, FILE* restrict stream);
|
||||
extern ssize_t getdelim(char** restrict lineptr, size_t* restrict n, int delimiter, FILE* restrict stream);
|
||||
extern ssize_t getline(char** restrict lineptr, size_t* restrict n, FILE* restrict stream);
|
||||
extern void clearerr(FILE* stream);
|
||||
extern void flockfile(FILE* file);
|
||||
extern void funlockfile(FILE* file);
|
||||
extern void rewind(FILE* stream);
|
||||
extern void setbuf(FILE* restrict stream, char* restrict buf);
|
||||
|
||||
#if __POSIX_OBSOLETE <= 200801
|
||||
|
@ -152,6 +151,13 @@ extern char* tempnam(const char* dir, const char* pfx);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SORTIX_EXTENSIONS
|
||||
void fregister(FILE* fp);
|
||||
void funregister(FILE* fp);
|
||||
FILE* fnewfile(void);
|
||||
int fcloseall(void);
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
@ -34,6 +34,7 @@ __BEGIN_DECLS
|
|||
@include(locale_t.h)
|
||||
|
||||
void* memcpy(void* restrict, const void* restrict, size_t);
|
||||
void* memset(void*, int, size_t);
|
||||
char* strcat(char* restrict, const char* restrict);
|
||||
int strcmp(const char*, const char*);
|
||||
char* strcpy(char* restrict, const char* restrict);
|
||||
|
@ -47,7 +48,6 @@ void* memccpy(void* restrict, const void* restrict, int, size_t);
|
|||
void* memchr(const void*, int, size_t);
|
||||
int memcmp(const void*, const void*, size_t);
|
||||
void* memmove(void*, const void*, size_t);
|
||||
void* memset(void*, int, size_t);
|
||||
char* stpcpy(char* restrict, const char* restrict);
|
||||
char* stpncpy(char* restrict, const char* restrict, size_t);
|
||||
char* strchr(const char*, int);
|
||||
|
|
39
libmaxsi/c/stdio.c
Normal file
39
libmaxsi/c/stdio.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/******************************************************************************
|
||||
|
||||
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
||||
|
||||
This file is part of LibMaxsi.
|
||||
|
||||
LibMaxsi is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
LibMaxsi 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 Lesser General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
stdio.c
|
||||
Sets up stdin, stdout, stderr.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "fdio.h"
|
||||
|
||||
FILE* stdin;
|
||||
FILE* stdout;
|
||||
FILE* stderr;
|
||||
|
||||
int init_stdio()
|
||||
{
|
||||
stdin = fdio_newfile(0, "r");
|
||||
stdout = fdio_newfile(1, "w");
|
||||
stderr = fdio_newfile(2, "w");
|
||||
return 0;
|
||||
}
|
|
@ -19,8 +19,8 @@ typedef __uintmax_t uintmax_t;
|
|||
|
||||
/* Define an integer able to hold the size of the largest continious memory */
|
||||
/* region and define pointer safe integer types. */
|
||||
typedef __size_t size_t;
|
||||
typedef __ssize_t ssize_t;
|
||||
@include(size_t.h)
|
||||
@include(ssize_t.h)
|
||||
typedef __intptr_t intptr_t;
|
||||
typedef __uintptr_t uintptr_t;
|
||||
typedef __ptrdiff_t ptrdiff_t;
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace Maxsi
|
|||
extern "C" { char* program_invocation_name = program_invocation_name_data; }
|
||||
|
||||
extern "C" void init_error_functions();
|
||||
extern "C" void init_stdio();
|
||||
|
||||
extern "C" void initialize_standard_library(int argc, char* argv[])
|
||||
{
|
||||
|
@ -51,5 +52,8 @@ namespace Maxsi
|
|||
|
||||
// Initialize the dynamic heap.
|
||||
Memory::Init();
|
||||
|
||||
// Initialize stdio.
|
||||
init_stdio();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <string.h>
|
||||
#include <error.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Maxsi
|
||||
{
|
||||
|
@ -72,11 +73,38 @@ namespace Maxsi
|
|||
}
|
||||
|
||||
#ifdef LIBMAXSI_LIBC
|
||||
size_t FileWriteCallback(void* user, const char* string, size_t stringlen)
|
||||
{
|
||||
FILE* fp = (FILE*) user;
|
||||
return fwrite(string, 1, stringlen, fp);
|
||||
}
|
||||
|
||||
extern "C" int vfprintf(FILE* fp, const char* /*restrict*/ format, va_list list)
|
||||
{
|
||||
size_t result = Maxsi::Format::Virtual(FileWriteCallback, fp, format, list);
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
extern "C" int fprintf(FILE* fp, const char* /*restrict*/ format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
size_t result = vfprintf(fp, format, list);
|
||||
va_end(list);
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
extern "C" int vprintf(const char* /*restrict*/ format, va_list list)
|
||||
{
|
||||
size_t result = vfprintf(stdout, format, list);
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
extern "C" int printf(const char* /*restrict*/ format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
size_t result = Maxsi::Format::Virtual(PrintCallback, NULL, format, list);
|
||||
size_t result = vprintf(format, list);
|
||||
va_end(list);
|
||||
return (int) result;
|
||||
}
|
||||
|
@ -87,7 +115,7 @@ namespace Maxsi
|
|||
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
size_t result = Maxsi::Format::Virtual(PrintCallback, NULL, format, list);
|
||||
vprintf(format, list);
|
||||
va_end(list);
|
||||
|
||||
printf(": %s\n", strerror(errnum));
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "platform.h"
|
||||
#include "syscall.h"
|
||||
#include "process.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Maxsi
|
||||
{
|
||||
|
@ -57,6 +58,7 @@ namespace Maxsi
|
|||
|
||||
DUAL_FUNCTION(void, exit, Exit, (int status))
|
||||
{
|
||||
fcloseall();
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,16 +62,17 @@ int main(int argc, char* argv[])
|
|||
|
||||
if ( codepoint == 0 ) { continue; }
|
||||
if ( codepoint & Maxsi::Keyboard::DEPRESSED ) { continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::UP ) { printf("\e[A"); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::DOWN ) { printf("\e[B"); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::RIGHT ) { printf("\e[C"); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::LEFT ) { printf("\e[D"); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::ESC ) { printf("\e["); lastwasesc = true; continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::UP ) { printf("\e[A"); fflush(stdout); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::DOWN ) { printf("\e[B"); fflush(stdout); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::RIGHT ) { printf("\e[C"); fflush(stdout); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::LEFT ) { printf("\e[D"); fflush(stdout); continue; }
|
||||
if ( codepoint == Maxsi::Keyboard::ESC ) { printf("\e["); fflush(stdout); lastwasesc = true; continue; }
|
||||
if ( lastwasesc && codepoint == '[' ) { continue; }
|
||||
if ( codepoint >= 0x80 ) { continue; }
|
||||
|
||||
char msg[2]; msg[0] = codepoint; msg[1] = '\0';
|
||||
printf(msg);
|
||||
fflush(stdout);
|
||||
lastwasesc = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
int main(int argc, char* argv[])
|
||||
{
|
||||
printf("\e[H\e[2J");
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ using namespace Maxsi;
|
|||
void cursorto(unsigned x, unsigned y)
|
||||
{
|
||||
printf("\e[%u;%uH", y+1+1, x+1);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void drawtextmode()
|
||||
|
@ -54,6 +55,7 @@ void drawtextmode()
|
|||
if ( y < HEIGHT-1 ) { printf("\n"); }
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
cursorto(cursorx, cursory);
|
||||
}
|
||||
|
||||
|
@ -125,7 +127,8 @@ unsigned textmode()
|
|||
buffers[cursory][x] = buffers[cursory][x+1];
|
||||
bufferchanged = true;
|
||||
}
|
||||
printf("\e[2K\r%s", buffers[cursory]);
|
||||
printf("\e[2K\r%s", buffers[cursory]);
|
||||
fflush(stdout);
|
||||
}
|
||||
else if ( 0 < cursory && strlen(buffers[cursory]) == 0 )
|
||||
{
|
||||
|
@ -161,6 +164,7 @@ unsigned textmode()
|
|||
msg[1] = 0;
|
||||
printf("%s", msg);
|
||||
buffers[cursory][cursorx++] = codepoint;
|
||||
fflush(stdout);
|
||||
bufferchanged = true;
|
||||
if ( WIDTH <= cursorx ) { cursorx = WIDTH-1; }
|
||||
}
|
||||
|
@ -230,6 +234,7 @@ int savemode()
|
|||
retry:
|
||||
size_t len = strlen(writefilename);
|
||||
printf("File Name to Write: %s", writefilename);
|
||||
fflush(stdout);
|
||||
|
||||
bool readytosave = false;
|
||||
|
||||
|
@ -247,7 +252,7 @@ retry:
|
|||
return MODE_TEXT;
|
||||
break;
|
||||
case '\b':
|
||||
if ( 0 < len ) { printf("\b"); writefilename[--len] = 0; }
|
||||
if ( 0 < len ) { printf("\b"); fflush(stdout); writefilename[--len] = 0; }
|
||||
break;
|
||||
case '\n':
|
||||
if ( len == 0 ) { return MODE_TEXT; }
|
||||
|
@ -262,6 +267,7 @@ retry:
|
|||
msg[0] = codepoint;
|
||||
msg[1] = 0;
|
||||
printf("%s", msg);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,6 +333,7 @@ int loadmode()
|
|||
retry:
|
||||
size_t len = strlen(loadfilename);
|
||||
printf("File Name to Load: %s", loadfilename);
|
||||
fflush(stdout);
|
||||
|
||||
bool readytoload = false;
|
||||
|
||||
|
@ -344,7 +351,7 @@ retry:
|
|||
return MODE_TEXT;
|
||||
break;
|
||||
case '\b':
|
||||
if ( 0 < len ) { printf("\b"); loadfilename[--len] = 0; }
|
||||
if ( 0 < len ) { printf("\b"); fflush(stdout); loadfilename[--len] = 0; }
|
||||
break;
|
||||
case '\n':
|
||||
if ( len == 0 ) { return MODE_TEXT; }
|
||||
|
@ -359,6 +366,7 @@ retry:
|
|||
msg[0] = codepoint;
|
||||
msg[1] = 0;
|
||||
printf("%s", msg);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,6 +414,7 @@ void run()
|
|||
}
|
||||
|
||||
printf("\e[37m\e[40m\e[2J\e[H");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
|
|
@ -37,6 +37,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
// Reset the terminal's color and the rest of it.
|
||||
printf("\r\e[m\e[J");
|
||||
fflush(stdout);
|
||||
|
||||
pid_t childpid = Process::Fork();
|
||||
if ( childpid < 0 ) { perror("fork"); return 2; }
|
||||
|
|
|
@ -22,7 +22,8 @@ void command()
|
|||
const char* wd = getcwd(cwd, CWD_SIZE);
|
||||
if ( !wd ) { wd = "?"; }
|
||||
|
||||
printf("root@sortix %s # ", wd);
|
||||
printf("root@sortix %s # ", wd);
|
||||
fflush(stdout);
|
||||
|
||||
const size_t commandsize = 128;
|
||||
char command[commandsize + 1];
|
||||
|
@ -39,7 +40,7 @@ void command()
|
|||
|
||||
if ( codepoint == '\b' )
|
||||
{
|
||||
if ( 0 < commandused ) { printf("\b"); commandused--; }
|
||||
if ( 0 < commandused ) { printf("\b"); fflush(stdout); commandused--; }
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -47,6 +48,7 @@ void command()
|
|||
|
||||
char msg[2]; msg[0] = codepoint; msg[1] = '\0';
|
||||
printf("%s", msg);
|
||||
fflush(stdout);
|
||||
|
||||
if ( codepoint == '\n' ) { command[commandused] = '\0'; break; }
|
||||
|
||||
|
|
Loading…
Reference in a new issue