2525 */
2626
2727#include <stdio.h>
28+ #include <errno.h>
2829
29- #include "misc.h"
3030#include "mpconfig.h"
31+ #include "nlr.h"
32+ #include "misc.h"
3133#include "qstr.h"
3234#include "obj.h"
3335#include "runtime.h"
3436#include "stream.h"
3537#include "file.h"
3638#include "ff.h"
3739
40+ extern const mp_obj_type_t mp_type_fileio ;
41+ extern const mp_obj_type_t mp_type_textio ;
42+
43+ // this table converts from FRESULT to POSIX errno
44+ STATIC const byte fresult_to_errno_table [] = {
45+ [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+
3867typedef struct _pyb_file_obj_t {
3968 mp_obj_base_t base ;
4069 FIL fp ;
@@ -44,17 +73,25 @@ void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, m
4473 printf ("<io.%s %p>" , mp_obj_get_type_str (self_in ), self_in );
4574}
4675
47- STATIC machine_int_t file_read (mp_obj_t self_in , void * buf , machine_uint_t size , int * errcode ) {
76+ STATIC machine_int_t file_obj_read (mp_obj_t self_in , void * buf , machine_uint_t size , int * errcode ) {
4877 pyb_file_obj_t * self = self_in ;
4978 UINT sz_out ;
50- * errcode = f_read (& self -> fp , buf , size , & sz_out );
79+ FRESULT res = f_read (& self -> fp , buf , size , & sz_out );
80+ if (res != FR_OK ) {
81+ * errcode = fresult_to_errno_table [res ];
82+ return -1 ;
83+ }
5184 return sz_out ;
5285}
5386
54- STATIC machine_int_t file_write (mp_obj_t self_in , const void * buf , machine_uint_t size , int * errcode ) {
87+ STATIC machine_int_t file_obj_write (mp_obj_t self_in , const void * buf , machine_uint_t size , int * errcode ) {
5588 pyb_file_obj_t * self = self_in ;
5689 UINT sz_out ;
57- * errcode = f_write (& self -> fp , buf , size , & sz_out );
90+ FRESULT res = f_write (& self -> fp , buf , size , & sz_out );
91+ if (res != FR_OK ) {
92+ * errcode = fresult_to_errno_table [res ];
93+ return -1 ;
94+ }
5895 return sz_out ;
5996}
6097
@@ -63,77 +100,163 @@ mp_obj_t file_obj_close(mp_obj_t self_in) {
63100 f_close (& self -> fp );
64101 return mp_const_none ;
65102}
66-
67103STATIC MP_DEFINE_CONST_FUN_OBJ_1 (file_obj_close_obj , file_obj_close );
68104
69105mp_obj_t file_obj___exit__ (uint n_args , const mp_obj_t * args ) {
70106 return file_obj_close (args [0 ]);
71107}
72- static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (file_obj___exit___obj , 4 , 4 , file_obj___exit__ ) ;
108+ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (file_obj___exit___obj , 4 , 4 , file_obj___exit__ );
109+
110+ mp_obj_t file_obj_seek (uint n_args , const mp_obj_t * args ) {
111+ pyb_file_obj_t * self = args [0 ];
112+ machine_int_t offset = mp_obj_get_int (args [1 ]);
113+ machine_int_t whence = 0 ;
114+ if (n_args == 3 ) {
115+ whence = mp_obj_get_int (args [2 ]);
116+ }
117+
118+ switch (whence ) {
119+ case 0 : // SEEK_SET
120+ f_lseek (& self -> fp , offset );
121+ break ;
122+
123+ case 1 : // SEEK_CUR
124+ if (offset != 0 ) {
125+ goto error ;
126+ }
127+ // no-operation
128+ break ;
129+
130+ case 2 : // SEEK_END
131+ if (offset != 0 ) {
132+ goto error ;
133+ }
134+ f_lseek (& self -> fp , f_size (& self -> fp ));
135+ break ;
136+
137+ default :
138+ goto error ;
139+ }
140+
141+ return mp_obj_new_int_from_uint (f_tell (& self -> fp ));
142+
143+ error :
144+ // A bad whence is a ValueError, while offset!=0 is an io.UnsupportedOperation.
145+ // But the latter inherits ValueError (as well as IOError), so we just raise ValueError.
146+ nlr_raise (mp_obj_new_exception_msg (& mp_type_ValueError , "invalid whence and/or offset" ));
147+ }
148+ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (file_obj_seek_obj , 2 , 3 , file_obj_seek );
149+
150+ mp_obj_t file_obj_tell (mp_obj_t self_in ) {
151+ pyb_file_obj_t * self = self_in ;
152+ return mp_obj_new_int_from_uint (f_tell (& self -> fp ));
153+ }
154+ STATIC MP_DEFINE_CONST_FUN_OBJ_1 (file_obj_tell_obj , file_obj_tell );
155+
156+ STATIC mp_obj_t file_obj_make_new (mp_obj_t type , uint n_args , uint n_kw , const mp_obj_t * args ) {
157+ mp_arg_check_num (n_args , n_kw , 1 , 2 , false);
158+
159+ const char * fname = mp_obj_str_get_str (args [0 ]);
160+
161+ int mode = 0 ;
162+ if (n_args == 1 ) {
163+ mode = FA_READ ;
164+ } else {
165+ const char * mode_s = mp_obj_str_get_str (args [1 ]);
166+ // TODO make sure only one of r, w, x, a, and b, t are specified
167+ while (* mode_s ) {
168+ switch (* mode_s ++ ) {
169+ case 'r' :
170+ mode |= FA_READ ;
171+ break ;
172+ case 'w' :
173+ mode |= FA_WRITE | FA_CREATE_ALWAYS ;
174+ break ;
175+ case 'x' :
176+ mode |= FA_WRITE | FA_CREATE_NEW ;
177+ break ;
178+ case 'a' :
179+ mode |= FA_WRITE | FA_OPEN_ALWAYS ;
180+ break ;
181+ case '+' :
182+ mode |= FA_READ | FA_WRITE ;
183+ break ;
184+ #if MICROPY_PY_IO_FILEIO
185+ case 'b' :
186+ type = (mp_obj_t )& mp_type_fileio ;
187+ break ;
188+ #endif
189+ case 't' :
190+ type = (mp_obj_t )& mp_type_textio ;
191+ break ;
192+ }
193+ }
194+ }
195+
196+ pyb_file_obj_t * o = m_new_obj_with_finaliser (pyb_file_obj_t );
197+ o -> base .type = type ;
198+
199+ FRESULT res = f_open (& o -> fp , fname , mode );
200+ if (res != FR_OK ) {
201+ m_del_obj (pyb_file_obj_t , o );
202+ nlr_raise (mp_obj_new_exception_arg1 (& mp_type_OSError , MP_OBJ_NEW_SMALL_INT ((machine_int_t )fresult_to_errno_table [res ])));
203+ }
204+
205+ return o ;
206+ }
73207
74208// TODO gc hook to close the file if not already closed
75209
76- STATIC const mp_map_elem_t file_locals_dict_table [] = {
210+ STATIC const mp_map_elem_t rawfile_locals_dict_table [] = {
77211 { MP_OBJ_NEW_QSTR (MP_QSTR_read ), (mp_obj_t )& mp_stream_read_obj },
78212 { MP_OBJ_NEW_QSTR (MP_QSTR_readall ), (mp_obj_t )& mp_stream_readall_obj },
79213 { MP_OBJ_NEW_QSTR (MP_QSTR_readline ), (mp_obj_t )& mp_stream_unbuffered_readline_obj },
80214 { MP_OBJ_NEW_QSTR (MP_QSTR_readlines ), (mp_obj_t )& mp_stream_unbuffered_readlines_obj },
81215 { MP_OBJ_NEW_QSTR (MP_QSTR_write ), (mp_obj_t )& mp_stream_write_obj },
82216 { MP_OBJ_NEW_QSTR (MP_QSTR_close ), (mp_obj_t )& file_obj_close_obj },
217+ { MP_OBJ_NEW_QSTR (MP_QSTR_seek ), (mp_obj_t )& file_obj_seek_obj },
218+ { MP_OBJ_NEW_QSTR (MP_QSTR_tell ), (mp_obj_t )& file_obj_tell_obj },
83219 { MP_OBJ_NEW_QSTR (MP_QSTR___del__ ), (mp_obj_t )& file_obj_close_obj },
84220 { MP_OBJ_NEW_QSTR (MP_QSTR___enter__ ), (mp_obj_t )& mp_identity_obj },
85221 { MP_OBJ_NEW_QSTR (MP_QSTR___exit__ ), (mp_obj_t )& file_obj___exit___obj },
86222};
87223
88- STATIC MP_DEFINE_CONST_DICT (file_locals_dict , file_locals_dict_table );
89-
90- STATIC mp_obj_t file_obj_make_new (mp_obj_t type_in , uint n_args , uint n_kw , const mp_obj_t * args );
224+ STATIC MP_DEFINE_CONST_DICT (rawfile_locals_dict , rawfile_locals_dict_table );
91225
92- STATIC const mp_stream_p_t file_obj_stream_p = {
93- .read = file_read ,
94- .write = file_write ,
226+ #if MICROPY_PY_IO_FILEIO
227+ STATIC const mp_stream_p_t fileio_stream_p = {
228+ .read = file_obj_read ,
229+ .write = file_obj_write ,
230+ .is_bytes = true,
95231};
96232
97- const mp_obj_type_t mp_type_textio = {
233+ const mp_obj_type_t mp_type_fileio = {
98234 { & mp_type_type },
99235 .name = MP_QSTR_FileIO ,
100- .make_new = file_obj_make_new ,
101236 .print = file_obj_print ,
237+ .make_new = file_obj_make_new ,
102238 .getiter = mp_identity ,
103239 .iternext = mp_stream_unbuffered_iter ,
104- .stream_p = & file_obj_stream_p ,
105- .locals_dict = (mp_obj_t )& file_locals_dict ,
240+ .stream_p = & fileio_stream_p ,
241+ .locals_dict = (mp_obj_t )& rawfile_locals_dict ,
106242};
243+ #endif
107244
108- STATIC mp_obj_t file_obj_make_new (mp_obj_t type_in , uint n_args , uint n_kw , const mp_obj_t * args ) {
109- mp_arg_check_num (n_args , n_kw , 1 , 2 , false);
110- const char * filename = mp_obj_str_get_str (args [0 ]);
111- const char * mode = "r" ;
112- if (n_args > 1 ) {
113- mode = mp_obj_str_get_str (args [1 ]);
114- }
115- pyb_file_obj_t * self = m_new_obj_with_finaliser (pyb_file_obj_t );
116- self -> base .type = & mp_type_textio ;
117- if (mode [0 ] == 'r' ) {
118- // open for reading
119- FRESULT res = f_open (& self -> fp , filename , FA_READ );
120- if (res != FR_OK ) {
121- printf ("FileNotFoundError: [Errno 2] No such file or directory: '%s'\n" , filename );
122- return mp_const_none ;
123- }
124- } else if (mode [0 ] == 'w' ) {
125- // open for writing, truncate the file first
126- FRESULT res = f_open (& self -> fp , filename , FA_WRITE | FA_CREATE_ALWAYS );
127- if (res != FR_OK ) {
128- printf ("?FileError: could not create file: '%s'\n" , filename );
129- return mp_const_none ;
130- }
131- } else {
132- printf ("ValueError: invalid mode: '%s'\n" , mode );
133- return mp_const_none ;
134- }
135- return self ;
136- }
245+ STATIC const mp_stream_p_t textio_stream_p = {
246+ .read = file_obj_read ,
247+ .write = file_obj_write ,
248+ };
249+
250+ const mp_obj_type_t mp_type_textio = {
251+ { & mp_type_type },
252+ .name = MP_QSTR_TextIOWrapper ,
253+ .print = file_obj_print ,
254+ .make_new = file_obj_make_new ,
255+ .getiter = mp_identity ,
256+ .iternext = mp_stream_unbuffered_iter ,
257+ .stream_p = & textio_stream_p ,
258+ .locals_dict = (mp_obj_t )& rawfile_locals_dict ,
259+ };
137260
138261// Factory function for I/O stream classes
139262STATIC mp_obj_t pyb_io_open (uint n_args , const mp_obj_t * args ) {
0 commit comments