paul@354 | 1 | /* Native functions for input/output. |
paul@354 | 2 | |
paul@569 | 3 | Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk> |
paul@354 | 4 | |
paul@354 | 5 | This program is free software; you can redistribute it and/or modify it under |
paul@354 | 6 | the terms of the GNU General Public License as published by the Free Software |
paul@354 | 7 | Foundation; either version 3 of the License, or (at your option) any later |
paul@354 | 8 | version. |
paul@354 | 9 | |
paul@354 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@354 | 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@354 | 12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@354 | 13 | details. |
paul@354 | 14 | |
paul@354 | 15 | You should have received a copy of the GNU General Public License along with |
paul@354 | 16 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@354 | 17 | */ |
paul@354 | 18 | |
paul@354 | 19 | #include <unistd.h> /* read, write */ |
paul@378 | 20 | #include <string.h> /* strcmp, memcpy */ |
paul@354 | 21 | #include <stdio.h> /* fdopen, snprintf */ |
paul@354 | 22 | #include <errno.h> /* errno */ |
paul@354 | 23 | #include "native/common.h" |
paul@354 | 24 | #include "types.h" |
paul@354 | 25 | #include "exceptions.h" |
paul@354 | 26 | #include "ops.h" |
paul@354 | 27 | #include "progconsts.h" |
paul@354 | 28 | #include "progops.h" |
paul@354 | 29 | #include "progtypes.h" |
paul@354 | 30 | #include "main.h" |
paul@354 | 31 | |
paul@354 | 32 | /* Input/output. */ |
paul@354 | 33 | |
paul@354 | 34 | __attr __fn_native_io_fclose(__attr __args[]) |
paul@354 | 35 | { |
paul@354 | 36 | __attr * const fp = &__args[1]; |
paul@354 | 37 | /* fp interpreted as FILE reference */ |
paul@354 | 38 | FILE *f = (FILE *) fp->datavalue; |
paul@354 | 39 | |
paul@354 | 40 | errno = 0; |
paul@354 | 41 | if (fclose(f)) |
paul@354 | 42 | __raise_io_error(__new_int(errno)); |
paul@354 | 43 | |
paul@354 | 44 | return __builtins___none_None; |
paul@354 | 45 | } |
paul@354 | 46 | |
paul@436 | 47 | __attr __fn_native_io_fflush(__attr __args[]) |
paul@436 | 48 | { |
paul@436 | 49 | __attr * const fp = &__args[1]; |
paul@436 | 50 | /* fp interpreted as FILE reference */ |
paul@436 | 51 | FILE *f = (FILE *) fp->datavalue; |
paul@436 | 52 | |
paul@436 | 53 | errno = 0; |
paul@436 | 54 | if (fflush(f)) |
paul@436 | 55 | __raise_io_error(__new_int(errno)); |
paul@436 | 56 | |
paul@436 | 57 | return __builtins___none_None; |
paul@436 | 58 | } |
paul@436 | 59 | |
paul@354 | 60 | __attr __fn_native_io_fopen(__attr __args[]) |
paul@354 | 61 | { |
paul@354 | 62 | __attr * const filename = &__args[1]; |
paul@354 | 63 | __attr * const mode = &__args[2]; |
paul@354 | 64 | /* filename.__data__ interpreted as string */ |
paul@758 | 65 | char *fn = __load_via_object(__VALUE(*filename), __data__).strvalue; |
paul@354 | 66 | /* mode.__data__ interpreted as string */ |
paul@758 | 67 | char *s = __load_via_object(__VALUE(*mode), __data__).strvalue; |
paul@354 | 68 | FILE *f; |
paul@354 | 69 | __attr attr; |
paul@354 | 70 | |
paul@354 | 71 | errno = 0; |
paul@354 | 72 | f = fopen(fn, s); |
paul@354 | 73 | |
paul@354 | 74 | /* Produce an exception if the operation failed. */ |
paul@354 | 75 | |
paul@354 | 76 | if (f == NULL) |
paul@354 | 77 | __raise_io_error(__new_int(errno)); |
paul@354 | 78 | |
paul@354 | 79 | /* Return the __data__ attribute. */ |
paul@354 | 80 | |
paul@354 | 81 | else |
paul@354 | 82 | { |
paul@354 | 83 | attr.datavalue = (void *) f; |
paul@354 | 84 | return attr; |
paul@354 | 85 | } |
paul@477 | 86 | |
paul@477 | 87 | /* Should never be reached: included to satisfy the compiler. */ |
paul@477 | 88 | |
paul@477 | 89 | return __builtins___none_None; |
paul@354 | 90 | } |
paul@354 | 91 | |
paul@354 | 92 | __attr __fn_native_io_fdopen(__attr __args[]) |
paul@354 | 93 | { |
paul@354 | 94 | __attr * const fd = &__args[1]; |
paul@354 | 95 | __attr * const mode = &__args[2]; |
paul@758 | 96 | /* fd interpreted as int */ |
paul@758 | 97 | int i = __TOINT(*fd); |
paul@354 | 98 | /* mode.__data__ interpreted as string */ |
paul@758 | 99 | char *s = __load_via_object(__VALUE(*mode), __data__).strvalue; |
paul@354 | 100 | FILE *f; |
paul@354 | 101 | __attr attr; |
paul@354 | 102 | |
paul@354 | 103 | errno = 0; |
paul@354 | 104 | f = fdopen(i, s); |
paul@354 | 105 | |
paul@354 | 106 | /* Produce an exception if the operation failed. */ |
paul@354 | 107 | |
paul@354 | 108 | if (f == NULL) |
paul@354 | 109 | __raise_io_error(__new_int(errno)); |
paul@354 | 110 | |
paul@354 | 111 | /* Return the __data__ attribute. */ |
paul@354 | 112 | |
paul@354 | 113 | else |
paul@354 | 114 | { |
paul@354 | 115 | attr.datavalue = (void *) f; |
paul@354 | 116 | return attr; |
paul@354 | 117 | } |
paul@477 | 118 | |
paul@477 | 119 | /* Should never be reached: included to satisfy the compiler. */ |
paul@477 | 120 | |
paul@477 | 121 | return __builtins___none_None; |
paul@354 | 122 | } |
paul@354 | 123 | |
paul@354 | 124 | __attr __fn_native_io_fread(__attr __args[]) |
paul@354 | 125 | { |
paul@354 | 126 | __attr * const fp = &__args[1]; |
paul@354 | 127 | __attr * const size = &__args[2]; |
paul@354 | 128 | /* fp interpreted as FILE reference */ |
paul@354 | 129 | FILE *f = (FILE *) fp->datavalue; |
paul@758 | 130 | /* size interpreted as int */ |
paul@758 | 131 | int to_read = __TOINT(*size); |
paul@354 | 132 | char buf[to_read]; |
paul@354 | 133 | size_t have_read; |
paul@354 | 134 | int error; |
paul@354 | 135 | char *s; |
paul@354 | 136 | |
paul@354 | 137 | have_read = fread(buf, sizeof(char), to_read, f); |
paul@354 | 138 | |
paul@354 | 139 | if (have_read != to_read) |
paul@354 | 140 | { |
paul@354 | 141 | if (feof(f) && (have_read == 0)) |
paul@354 | 142 | __raise_eof_error(); |
paul@477 | 143 | else if ((error = ferror(f))) |
paul@354 | 144 | __raise_io_error(__new_int(error)); |
paul@354 | 145 | } |
paul@354 | 146 | |
paul@354 | 147 | /* Reserve space for a new string. */ |
paul@354 | 148 | |
paul@354 | 149 | s = __ALLOCATE(have_read + 1, sizeof(char)); |
paul@378 | 150 | memcpy(s, (char *) buf, have_read); /* does not null terminate but final byte should be zero */ |
paul@583 | 151 | return __new_str(s, have_read); |
paul@354 | 152 | } |
paul@354 | 153 | |
paul@354 | 154 | __attr __fn_native_io_fwrite(__attr __args[]) |
paul@354 | 155 | { |
paul@354 | 156 | __attr * const fp = &__args[1]; |
paul@354 | 157 | __attr * const str = &__args[2]; |
paul@354 | 158 | /* fp interpreted as FILE reference */ |
paul@354 | 159 | FILE *f = (FILE *) fp->datavalue; |
paul@354 | 160 | /* str.__data__ interpreted as string */ |
paul@758 | 161 | char *s = __load_via_object(__VALUE(*str), __data__).strvalue; |
paul@583 | 162 | /* str.__size__ interpreted as int */ |
paul@758 | 163 | int to_write = __TOINT(__load_via_object(__VALUE(*str), __size__)); |
paul@354 | 164 | size_t have_written = fwrite(s, sizeof(char), to_write, f); |
paul@354 | 165 | int error; |
paul@354 | 166 | |
paul@354 | 167 | if (have_written != to_write) |
paul@354 | 168 | { |
paul@354 | 169 | if (feof(f)) |
paul@354 | 170 | __raise_eof_error(); |
paul@477 | 171 | else if ((error = ferror(f))) |
paul@354 | 172 | __raise_io_error(__new_int(error)); |
paul@354 | 173 | } |
paul@354 | 174 | |
paul@354 | 175 | return __builtins___none_None; |
paul@354 | 176 | } |
paul@354 | 177 | |
paul@354 | 178 | __attr __fn_native_io_close(__attr __args[]) |
paul@354 | 179 | { |
paul@354 | 180 | __attr * const fd = &__args[1]; |
paul@758 | 181 | /* fd interpreted as int */ |
paul@758 | 182 | int i = __TOINT(*fd); |
paul@354 | 183 | |
paul@354 | 184 | errno = 0; |
paul@354 | 185 | if (close(i) == -1) |
paul@354 | 186 | __raise_io_error(__new_int(errno)); |
paul@354 | 187 | |
paul@354 | 188 | return __builtins___none_None; |
paul@354 | 189 | } |
paul@354 | 190 | |
paul@354 | 191 | __attr __fn_native_io_read(__attr __args[]) |
paul@354 | 192 | { |
paul@354 | 193 | __attr * const fd = &__args[1]; |
paul@354 | 194 | __attr * const n = &__args[2]; |
paul@758 | 195 | /* fd interpreted as int */ |
paul@758 | 196 | int i = __TOINT(*fd); |
paul@758 | 197 | /* n interpreted as int */ |
paul@758 | 198 | int to_read = __TOINT(*n); |
paul@354 | 199 | char buf[to_read]; |
paul@354 | 200 | ssize_t have_read; |
paul@354 | 201 | char *s; |
paul@354 | 202 | |
paul@354 | 203 | errno = 0; |
paul@354 | 204 | have_read = read(i, buf, to_read * sizeof(char)); |
paul@354 | 205 | |
paul@354 | 206 | if (have_read == -1) |
paul@354 | 207 | __raise_io_error(__new_int(errno)); |
paul@354 | 208 | |
paul@354 | 209 | /* Reserve space for a new string. */ |
paul@354 | 210 | |
paul@354 | 211 | s = __ALLOCATE(have_read + 1, 1); |
paul@378 | 212 | memcpy(s, (char *) buf, have_read); /* does not null terminate but final byte should be zero */ |
paul@583 | 213 | return __new_str(s, have_read); |
paul@354 | 214 | } |
paul@354 | 215 | |
paul@354 | 216 | __attr __fn_native_io_write(__attr __args[]) |
paul@354 | 217 | { |
paul@354 | 218 | __attr * const fd = &__args[1]; |
paul@354 | 219 | __attr * const str = &__args[2]; |
paul@758 | 220 | /* fd interpreted as int */ |
paul@758 | 221 | int i = __TOINT(*fd); |
paul@354 | 222 | /* str.__data__ interpreted as string */ |
paul@758 | 223 | char *s = __load_via_object(__VALUE(*str), __data__).strvalue; |
paul@583 | 224 | /* str.__size__ interpreted as int */ |
paul@758 | 225 | int size = __TOINT(__load_via_object(__VALUE(*str), __size__)); |
paul@354 | 226 | ssize_t have_written; |
paul@354 | 227 | |
paul@354 | 228 | errno = 0; |
paul@583 | 229 | have_written = write(i, s, sizeof(char) * size); |
paul@354 | 230 | |
paul@354 | 231 | if (have_written == -1) |
paul@354 | 232 | __raise_io_error(__new_int(errno)); |
paul@354 | 233 | |
paul@354 | 234 | return __new_int(have_written); |
paul@354 | 235 | } |
paul@354 | 236 | |
paul@354 | 237 | /* Module initialisation. */ |
paul@354 | 238 | |
paul@354 | 239 | void __main_native_io() |
paul@354 | 240 | { |
paul@354 | 241 | } |