123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- /*
- * io.c
- *
- * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <stdio.h>
- #include <unistd.h>
- #include "io_priv.h"
- static list_entry_t open_files;
- FILE *stdin = NULL;
- FILE *stdout = NULL;
- FILE *stderr = NULL;
- int fileno_unlocked(FILE *stream)
- {
- if (stream->fd == -1) errno = EBADF;
- return stream->fd;
- }
- int fileno(FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- int ret = fileno_unlocked(stream);
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- static int __crt_fflush_stream(FILE *stream)
- {
- if (!stream->buffer) return EOF;
- if (!(stream->flags & FILE_BUFFER_DIR))
- {
- stream->buffer_start = stream->buffer_end = 0;
- stream->flags &= ~FILE_BUFFER_FULL;
- return 0;
- }
- if (stream->fd == -1) return 0;
- if (stream->buffer_start < stream->buffer_end)
- {
- ssize_t written = write(stream->fd, &stream->buffer[stream->buffer_start], stream->buffer_end - stream->buffer_start);
- if (written < 0) return EOF;
- stream->buffer_start += written;
- stream->buffer_start %= stream->buffer_size;
- if (written) stream->flags &= ~FILE_BUFFER_FULL;
- }
- else
- {
- if (stream->buffer_start < stream->buffer_size)
- {
- ssize_t written = write(stream->fd, &stream->buffer[stream->buffer_start], stream->buffer_size - stream->buffer_start);
- if (written < 0) return EOF;
- stream->buffer_start += written;
- stream->buffer_start %= stream->buffer_size;
- if (written) stream->flags &= ~FILE_BUFFER_FULL;
- }
- if (stream->buffer_end)
- {
- ssize_t written = write(stream->fd, stream->buffer, stream->buffer_end);
- if (written < 0) return EOF;
- stream->buffer_start += written;
- stream->buffer_start %= stream->buffer_size;
- if (written) stream->flags &= ~FILE_BUFFER_FULL;
- }
- }
- return 0;
- }
- int fflush_unlocked(FILE *stream)
- {
- if (stream) return __crt_fflush_stream(stream);
- list_entry_t *entry;
- int result = 0;
- for (entry = open_files.next; entry != &open_files; entry = entry->next)
- {
- if (__crt_fflush_stream(CONTAINER_OF(entry, struct __crt_file, link)) == EOF)
- {
- result = EOF;
- }
- }
- return result;
- }
- int fflush(FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- int ret = fflush_unlocked(stream);
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- int setvbuf(FILE *stream, char *buf, int mode, size_t size)
- {
- int ret = EOF;
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- switch (mode)
- {
- case _IONBF:
- if (fflush_unlocked(stream) == 0)
- {
- stream->buffer = NULL;
- stream->buffer_size = 0;
- ret = 0;
- }
- break;
- case _IOLBF:
- if (!buf || fflush_unlocked(stream) == 0)
- {
- if (buf)
- {
- stream->buffer = buf;
- stream->buffer_size = size;
- }
- stream->flags |= FILE_BUFFER_LINE;
- ret = 0;
- }
- break;
- case _IOFBF:
- if (!buf || fflush_unlocked(stream) == 0)
- {
- if (buf)
- {
- stream->buffer = buf;
- stream->buffer_size = size;
- }
- stream->flags &= ~FILE_BUFFER_LINE;
- ret = 0;
- }
- break;
- }
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- void setbuf(FILE *stream, char *buf)
- {
- setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
- }
- void setbuffer(FILE *stream, char *buf, size_t size)
- {
- setvbuf(stream, buf, buf ? _IOFBF : _IONBF, size);
- }
- void setlinebuf(FILE *stream)
- {
- setvbuf(stream, NULL, _IOLBF, 0);
- }
- inline int fgetc_unlocked(FILE *stream)
- {
- char c;
- if (!(stream->flags & FILE_READ))
- {
- errno = EPERM;
- return EOF;
- }
- if (stream->buffer)
- {
- if (stream->flags & FILE_BUFFER_DIR)
- {
- fflush_unlocked(stream);
- stream->flags &= ~FILE_BUFFER_DIR;
- }
- if (stream->buffer_start == stream->buffer_end && !(stream->flags & FILE_BUFFER_FULL))
- {
- if (stream->fd == -1) return EOF;
- if (stream->buffer_end < stream->buffer_size)
- {
- ssize_t amount = read(stream->fd, &stream->buffer[stream->buffer_end], stream->buffer_size - stream->buffer_end);
- if (amount < 0) return EOF;
- stream->buffer_end += amount;
- stream->buffer_end %= stream->buffer_size;
- if (amount && (stream->buffer_start == stream->buffer_end)) stream->flags |= FILE_BUFFER_FULL;
- }
- if (stream->buffer_end < stream->buffer_start)
- {
- ssize_t amount = read(stream->fd, &stream->buffer[stream->buffer_end], stream->buffer_start - stream->buffer_end);
- if (amount < 0) return EOF;
- stream->buffer_end += amount;
- stream->buffer_end %= stream->buffer_size;
- if (amount && (stream->buffer_start == stream->buffer_end)) stream->flags |= FILE_BUFFER_FULL;
- }
- }
- c = stream->buffer[stream->buffer_start];
- stream->buffer_start++;
- stream->buffer_end %= stream->buffer_size;
- stream->flags &= ~FILE_BUFFER_FULL;
- }
- else
- {
- if (read(stream->fd, &c, 1) != 1) c = EOF;
- }
- return (int)((unsigned int)c);
- }
- int fgetc(FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- int ret = fgetc_unlocked(stream);
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- inline int fputc_unlocked(int c, FILE *stream)
- {
- if (!(stream->flags & FILE_WRITE))
- {
- errno = EPERM;
- return EOF;
- }
- if (stream->buffer)
- {
- if (!(stream->flags & FILE_BUFFER_DIR))
- {
- fflush_unlocked(stream);
- stream->flags |= FILE_BUFFER_DIR;
- }
- if (stream->flags & FILE_BUFFER_FULL)
- {
- errno = ENOSPC;
- return EOF;
- }
- stream->buffer[stream->buffer_end] = (uint8_t)c;
- stream->buffer_end++;
- stream->buffer_end %= stream->buffer_size;
- if (stream->buffer_start == stream->buffer_end)
- {
- stream->flags |= FILE_BUFFER_FULL;
- }
- if (stream->fd != -1 && ((stream->flags & FILE_BUFFER_FULL) || ((stream->flags & FILE_BUFFER_LINE) && (char)c == '\n')))
- {
- fflush_unlocked(stream);
- }
- }
- else
- {
- if (write(stream->fd, &c, 1) != 1) return EOF;
- }
- return 0;
- }
- int fputc(int c, FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- int ret = fputc_unlocked(c, stream);
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- char *fgets_unlocked(char *s, int size, FILE *stream)
- {
- int c;
- char *ptr = s;
- while (size-- > 0 && (c = fgetc_unlocked(stream)) != EOF)
- {
- *ptr++ = (char)c;
- if (c == '\n') break;
- }
- return (s != ptr) ? s : NULL;
- }
- char *fgets(char *s, int size, FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- char *ret = fgets_unlocked(s, size, stream);
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- int fputs_unlocked(const char *s, FILE *stream)
- {
- const char *ptr = s;
- while (*ptr) if (fputc_unlocked(*ptr++, stream) == EOF) return EOF;
- return 0;
- }
- int fputs(const char *s, FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- int ret = fputs_unlocked(s, stream);
- syscall_release_mutex(stream->mutex);
- return ret;
- }
- int ungetc(int c, FILE *stream)
- {
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- if (stream->flags & FILE_BUFFER_DIR)
- {
- fflush_unlocked(stream);
- stream->flags &= ~FILE_BUFFER_DIR;
- }
- if (stream->buffer_start == 0) stream->buffer_start = stream->buffer_size;
- stream->buffer_start--;
- stream->buffer[stream->buffer_start] = (uint8_t)c;
- syscall_release_mutex(stream->mutex);
- return c;
- }
- char *gets(char *s)
- {
- char *ptr = s;
- int c;
- while ((c = getchar()) != EOF) *ptr++ = (char)c;
- return ptr != s ? s : NULL;
- }
- int puts(const char *s)
- {
- if (fputs(s, stdout) == EOF) return EOF;
- if (fputc('\n', stdout) == EOF) return EOF;
- return 0;
- }
- FILE *fdopen(int fd, const char *mode)
- {
- FILE *stream = (FILE*)malloc(sizeof(FILE) + BUFSIZ);
- if (stream == NULL)
- {
- errno = ENOMEM;
- return NULL;
- }
- sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
- if (status != ERR_SUCCESS)
- {
- free(stream);
- errno = __crt_translate_error(status);
- return NULL;
- }
- stream->flags = 0;
- const char *ptr;
- for (ptr = mode; *ptr; ptr++)
- {
- switch (*ptr)
- {
- case 'r':
- stream->flags |= FILE_READ;
- break;
- case 'w':
- stream->flags |= FILE_WRITE;
- break;
- case 'a':
- stream->flags |= FILE_APPEND;
- break;
- case '+':
- stream->flags |= FILE_READ | FILE_WRITE;
- break;
- }
- }
- stream->fd = fd;
- char *buffer = (char*)((uintptr_t)stream + sizeof(FILE));
- setbuf(stream, buffer);
- return stream;
- }
- FILE *fmemopen(void *buf, size_t size, const char *mode)
- {
- FILE *stream = (FILE*)malloc(sizeof(FILE));
- if (stream == NULL)
- {
- errno = ENOMEM;
- return NULL;
- }
- sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
- if (status != ERR_SUCCESS)
- {
- free(stream);
- errno = __crt_translate_error(status);
- return NULL;
- }
- stream->flags = 0;
- const char *ptr;
- for (ptr = mode; *ptr; ptr++)
- {
- switch (*ptr)
- {
- case 'r':
- stream->flags |= FILE_READ;
- break;
- case 'w':
- stream->flags |= FILE_WRITE;
- break;
- case 'a':
- stream->flags |= FILE_APPEND;
- break;
- case '+':
- stream->flags |= FILE_READ | FILE_WRITE;
- break;
- }
- }
- stream->fd = -1;
- stream->buffer = buf;
- stream->buffer_size = size;
- stream->buffer_start = stream->buffer_end = 0;
- list_append(&open_files, &stream->link);
- return stream;
- }
- FILE *fopen(const char *pathname, const char *mode)
- {
- int open_flags = 0;
- const char *ptr;
- for (ptr = mode; *ptr; ptr++)
- {
- switch (*ptr)
- {
- case 'r':
- open_flags = (open_flags & ~3) | O_RDONLY;
- break;
- case 'w':
- open_flags = (open_flags & ~3) | O_WRONLY;
- break;
- case 'a':
- open_flags |= O_APPEND;
- break;
- case '+':
- open_flags = (open_flags & ~3) | O_RDWR;
- break;
- }
- }
- int fd = open(pathname, open_flags);
- if (fd < 0) return NULL;
- FILE *stream = fdopen(fd, mode);
- if (!stream) close(fd);
- return NULL;
- }
- int fclose(FILE *stream)
- {
- fflush(stream);
- list_remove(&stream->link);
- syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
- if (stream->fd) close(stream->fd);
- syscall_close_object(stream->mutex);
- free(stream);
- return 0;
- }
|