blob: 7ce8ef43f69faa02c422e69c62d9f98182ff1306 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Damien Georgeb92d3e12014-03-17 14:04:19 +000027#include <stdio.h>
Damien Georgeb7572ad2014-06-11 15:41:14 +010028#include <errno.h>
Damien Georgeb92d3e12014-03-17 14:04:19 +000029
Damien Georgeb92d3e12014-03-17 14:04:19 +000030#include "mpconfig.h"
Damien Georgeb7572ad2014-06-11 15:41:14 +010031#include "nlr.h"
32#include "misc.h"
Damien Georgeb92d3e12014-03-17 14:04:19 +000033#include "qstr.h"
34#include "obj.h"
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +030035#include "runtime.h"
36#include "stream.h"
Damien Georgeb92d3e12014-03-17 14:04:19 +000037#include "file.h"
38#include "ff.h"
39
Damien Georgeb7572ad2014-06-11 15:41:14 +010040extern const mp_obj_type_t mp_type_fileio;
41extern const mp_obj_type_t mp_type_textio;
42
43// this table converts from FRESULT to POSIX errno
Damien George02bc8822014-07-19 16:39:13 +010044const byte fresult_to_errno_table[20] = {
Damien Georgeb7572ad2014-06-11 15:41:14 +010045 [FR_OK] = 0,
46 [FR_DISK_ERR] = EIO,
47 [FR_INT_ERR] = EIO,
48 [FR_NOT_READY] = EBUSY,
49 [FR_NO_FILE] = ENOENT,
50 [FR_NO_PATH] = ENOENT,
51 [FR_INVALID_NAME] = EINVAL,
52 [FR_DENIED] = EACCES,
53 [FR_EXIST] = EEXIST,
54 [FR_INVALID_OBJECT] = EINVAL,
55 [FR_WRITE_PROTECTED] = EROFS,
56 [FR_INVALID_DRIVE] = ENODEV,
57 [FR_NOT_ENABLED] = ENODEV,
58 [FR_NO_FILESYSTEM] = ENODEV,
59 [FR_MKFS_ABORTED] = EIO,
60 [FR_TIMEOUT] = EIO,
61 [FR_LOCKED] = EIO,
62 [FR_NOT_ENOUGH_CORE] = ENOMEM,
63 [FR_TOO_MANY_OPEN_FILES] = EMFILE,
64 [FR_INVALID_PARAMETER] = EINVAL,
65};
66
Damien Georgeb92d3e12014-03-17 14:04:19 +000067typedef struct _pyb_file_obj_t {
68 mp_obj_base_t base;
69 FIL fp;
70} pyb_file_obj_t;
71
72void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
Damien Georgee79c6692014-06-15 09:10:07 +010073 print(env, "<io.%s %p>", mp_obj_get_type_str(self_in), self_in);
Damien Georgeb92d3e12014-03-17 14:04:19 +000074}
75
Damien Georgeadf0f2a2014-07-27 22:38:58 +010076STATIC mp_uint_t file_obj_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
Damien Georgeb92d3e12014-03-17 14:04:19 +000077 pyb_file_obj_t *self = self_in;
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +030078 UINT sz_out;
Damien Georgeb7572ad2014-06-11 15:41:14 +010079 FRESULT res = f_read(&self->fp, buf, size, &sz_out);
80 if (res != FR_OK) {
81 *errcode = fresult_to_errno_table[res];
Damien Georgeadf0f2a2014-07-27 22:38:58 +010082 return MP_STREAM_ERROR;
Damien Georgeb7572ad2014-06-11 15:41:14 +010083 }
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +030084 return sz_out;
Damien Georgeb92d3e12014-03-17 14:04:19 +000085}
86
Damien Georgeadf0f2a2014-07-27 22:38:58 +010087STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
Damien Georgeb92d3e12014-03-17 14:04:19 +000088 pyb_file_obj_t *self = self_in;
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +030089 UINT sz_out;
Damien Georgeb7572ad2014-06-11 15:41:14 +010090 FRESULT res = f_write(&self->fp, buf, size, &sz_out);
91 if (res != FR_OK) {
92 *errcode = fresult_to_errno_table[res];
Damien Georgeadf0f2a2014-07-27 22:38:58 +010093 return MP_STREAM_ERROR;
Damien Georgeb7572ad2014-06-11 15:41:14 +010094 }
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +030095 return sz_out;
Damien Georgeb92d3e12014-03-17 14:04:19 +000096}
97
Damien George02bc8822014-07-19 16:39:13 +010098STATIC mp_obj_t file_obj_flush(mp_obj_t self_in) {
99 pyb_file_obj_t *self = self_in;
100 f_sync(&self->fp);
101 return mp_const_none;
102}
103STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_flush_obj, file_obj_flush);
104
Damien Georgeb92d3e12014-03-17 14:04:19 +0000105mp_obj_t file_obj_close(mp_obj_t self_in) {
106 pyb_file_obj_t *self = self_in;
107 f_close(&self->fp);
108 return mp_const_none;
109}
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300110STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close);
111
112mp_obj_t file_obj___exit__(uint n_args, const mp_obj_t *args) {
113 return file_obj_close(args[0]);
114}
Damien Georgeb7572ad2014-06-11 15:41:14 +0100115STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj___exit___obj, 4, 4, file_obj___exit__);
116
117mp_obj_t file_obj_seek(uint n_args, const mp_obj_t *args) {
118 pyb_file_obj_t *self = args[0];
Damien George40f3c022014-07-03 13:25:24 +0100119 mp_int_t offset = mp_obj_get_int(args[1]);
120 mp_int_t whence = 0;
Damien Georgeb7572ad2014-06-11 15:41:14 +0100121 if (n_args == 3) {
122 whence = mp_obj_get_int(args[2]);
123 }
124
125 switch (whence) {
126 case 0: // SEEK_SET
127 f_lseek(&self->fp, offset);
128 break;
129
130 case 1: // SEEK_CUR
131 if (offset != 0) {
132 goto error;
133 }
134 // no-operation
135 break;
136
137 case 2: // SEEK_END
138 if (offset != 0) {
139 goto error;
140 }
141 f_lseek(&self->fp, f_size(&self->fp));
142 break;
143
144 default:
145 goto error;
146 }
147
148 return mp_obj_new_int_from_uint(f_tell(&self->fp));
149
150error:
151 // A bad whence is a ValueError, while offset!=0 is an io.UnsupportedOperation.
152 // But the latter inherits ValueError (as well as IOError), so we just raise ValueError.
153 nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "invalid whence and/or offset"));
154}
155STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(file_obj_seek_obj, 2, 3, file_obj_seek);
156
157mp_obj_t file_obj_tell(mp_obj_t self_in) {
158 pyb_file_obj_t *self = self_in;
159 return mp_obj_new_int_from_uint(f_tell(&self->fp));
160}
161STATIC MP_DEFINE_CONST_FUN_OBJ_1(file_obj_tell_obj, file_obj_tell);
162
163STATIC mp_obj_t file_obj_make_new(mp_obj_t type, uint n_args, uint n_kw, const mp_obj_t *args) {
164 mp_arg_check_num(n_args, n_kw, 1, 2, false);
165
166 const char *fname = mp_obj_str_get_str(args[0]);
167
168 int mode = 0;
169 if (n_args == 1) {
170 mode = FA_READ;
171 } else {
172 const char *mode_s = mp_obj_str_get_str(args[1]);
173 // TODO make sure only one of r, w, x, a, and b, t are specified
174 while (*mode_s) {
175 switch (*mode_s++) {
176 case 'r':
177 mode |= FA_READ;
178 break;
179 case 'w':
180 mode |= FA_WRITE | FA_CREATE_ALWAYS;
181 break;
182 case 'x':
183 mode |= FA_WRITE | FA_CREATE_NEW;
184 break;
185 case 'a':
186 mode |= FA_WRITE | FA_OPEN_ALWAYS;
187 break;
188 case '+':
189 mode |= FA_READ | FA_WRITE;
190 break;
191 #if MICROPY_PY_IO_FILEIO
192 case 'b':
193 type = (mp_obj_t)&mp_type_fileio;
194 break;
195 #endif
196 case 't':
197 type = (mp_obj_t)&mp_type_textio;
198 break;
199 }
200 }
201 }
202
203 pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t);
204 o->base.type = type;
205
206 FRESULT res = f_open(&o->fp, fname, mode);
207 if (res != FR_OK) {
208 m_del_obj(pyb_file_obj_t, o);
Damien Georgebb4c6f32014-07-31 10:49:14 +0100209 nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res])));
Damien Georgeb7572ad2014-06-11 15:41:14 +0100210 }
211
Damien George82089672014-06-11 16:01:52 +0100212 // for 'a' mode, we must begin at the end of the file
213 if ((mode & FA_OPEN_ALWAYS) != 0) {
214 f_lseek(&o->fp, f_size(&o->fp));
215 }
216
Damien Georgeb7572ad2014-06-11 15:41:14 +0100217 return o;
218}
Damien Georgeb92d3e12014-03-17 14:04:19 +0000219
220// TODO gc hook to close the file if not already closed
221
Damien Georgeb7572ad2014-06-11 15:41:14 +0100222STATIC const mp_map_elem_t rawfile_locals_dict_table[] = {
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300223 { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
224 { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj },
225 { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj},
Damien Georged5f5b2f2014-05-03 22:01:32 +0100226 { MP_OBJ_NEW_QSTR(MP_QSTR_readlines), (mp_obj_t)&mp_stream_unbuffered_readlines_obj},
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300227 { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
Damien George02bc8822014-07-19 16:39:13 +0100228 { MP_OBJ_NEW_QSTR(MP_QSTR_flush), (mp_obj_t)&file_obj_flush_obj },
Damien George9b196cd2014-03-26 21:47:19 +0000229 { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&file_obj_close_obj },
Damien Georgeb7572ad2014-06-11 15:41:14 +0100230 { MP_OBJ_NEW_QSTR(MP_QSTR_seek), (mp_obj_t)&file_obj_seek_obj },
231 { MP_OBJ_NEW_QSTR(MP_QSTR_tell), (mp_obj_t)&file_obj_tell_obj },
Damien George12bab722014-04-05 20:35:48 +0100232 { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&file_obj_close_obj },
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300233 { MP_OBJ_NEW_QSTR(MP_QSTR___enter__), (mp_obj_t)&mp_identity_obj },
234 { MP_OBJ_NEW_QSTR(MP_QSTR___exit__), (mp_obj_t)&file_obj___exit___obj },
Damien Georgeb92d3e12014-03-17 14:04:19 +0000235};
236
Damien Georgeb7572ad2014-06-11 15:41:14 +0100237STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table);
Damien George9b196cd2014-03-26 21:47:19 +0000238
Damien Georgeb7572ad2014-06-11 15:41:14 +0100239#if MICROPY_PY_IO_FILEIO
240STATIC const mp_stream_p_t fileio_stream_p = {
241 .read = file_obj_read,
242 .write = file_obj_write,
Damien Georgeb7572ad2014-06-11 15:41:14 +0100243};
Damien George27e735f2014-04-05 23:02:23 +0100244
Damien Georgeb7572ad2014-06-11 15:41:14 +0100245const mp_obj_type_t mp_type_fileio = {
246 { &mp_type_type },
247 .name = MP_QSTR_FileIO,
248 .print = file_obj_print,
249 .make_new = file_obj_make_new,
250 .getiter = mp_identity,
251 .iternext = mp_stream_unbuffered_iter,
252 .stream_p = &fileio_stream_p,
253 .locals_dict = (mp_obj_t)&rawfile_locals_dict,
254};
255#endif
256
257STATIC const mp_stream_p_t textio_stream_p = {
258 .read = file_obj_read,
259 .write = file_obj_write,
Damien Georgeadf0f2a2014-07-27 22:38:58 +0100260 .is_text = true,
Damien George27e735f2014-04-05 23:02:23 +0100261};
262
Paul Sokolovsky9e296662014-05-19 20:59:13 +0300263const mp_obj_type_t mp_type_textio = {
Damien Georgeb92d3e12014-03-17 14:04:19 +0000264 { &mp_type_type },
Damien Georgeb7572ad2014-06-11 15:41:14 +0100265 .name = MP_QSTR_TextIOWrapper,
Damien Georgeb92d3e12014-03-17 14:04:19 +0000266 .print = file_obj_print,
Damien Georgeb7572ad2014-06-11 15:41:14 +0100267 .make_new = file_obj_make_new,
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300268 .getiter = mp_identity,
269 .iternext = mp_stream_unbuffered_iter,
Damien Georgeb7572ad2014-06-11 15:41:14 +0100270 .stream_p = &textio_stream_p,
271 .locals_dict = (mp_obj_t)&rawfile_locals_dict,
Damien Georgeb92d3e12014-03-17 14:04:19 +0000272};
273
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300274// Factory function for I/O stream classes
275STATIC mp_obj_t pyb_io_open(uint n_args, const mp_obj_t *args) {
276 // TODO: analyze mode and buffering args and instantiate appropriate type
Paul Sokolovsky9e296662014-05-19 20:59:13 +0300277 return file_obj_make_new((mp_obj_t)&mp_type_textio, n_args, 0, args);
Paul Sokolovsky1d4d9dd2014-04-03 20:32:54 +0300278}
279
280MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, pyb_io_open);