1 /**
2  * @file lv_fs_stdio.c
3  *
4  */
5 
6 
7 /*********************
8  *      INCLUDES
9  *********************/
10 #include "../../../lvgl.h"
11 #if LV_USE_FS_STDIO != '\0'
12 
13 #include <stdio.h>
14 #ifndef WIN32
15     #include <dirent.h>
16     #include <unistd.h>
17 #else
18     #include <windows.h>
19 #endif
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 #define MAX_PATH_LEN 256
25 
26 /**********************
27  *      TYPEDEFS
28  **********************/
29 typedef struct {
30 #ifdef WIN32
31     HANDLE dir_p;
32     char next_fn[MAX_PATH_LEN];
33 #else
34     DIR * dir_p;
35 #endif
36 } dir_handle_t;
37 
38 /**********************
39  *  STATIC PROTOTYPES
40  **********************/
41 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
42 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
43 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
44 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
45 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
46 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
47 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
48 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
49 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
50 
51 /**********************
52  *  STATIC VARIABLES
53  **********************/
54 
55 /**********************
56  *      MACROS
57  **********************/
58 
59 /**********************
60  *   GLOBAL FUNCTIONS
61  **********************/
62 
63 /**
64  * Register a driver for the File system interface
65  */
lv_fs_stdio_init(void)66 void lv_fs_stdio_init(void)
67 {
68     /*---------------------------------------------------
69      * Register the file system interface in LVGL
70      *--------------------------------------------------*/
71 
72     /*Add a simple drive to open images*/
73     static lv_fs_drv_t fs_drv; /*A driver descriptor*/
74     lv_fs_drv_init(&fs_drv);
75 
76     /*Set up fields...*/
77     fs_drv.letter = LV_FS_STDIO_LETTER;
78     fs_drv.cache_size = LV_FS_STDIO_CACHE_SIZE;
79 
80     fs_drv.open_cb = fs_open;
81     fs_drv.close_cb = fs_close;
82     fs_drv.read_cb = fs_read;
83     fs_drv.write_cb = fs_write;
84     fs_drv.seek_cb = fs_seek;
85     fs_drv.tell_cb = fs_tell;
86 
87     fs_drv.dir_close_cb = fs_dir_close;
88     fs_drv.dir_open_cb = fs_dir_open;
89     fs_drv.dir_read_cb = fs_dir_read;
90 
91     lv_fs_drv_register(&fs_drv);
92 }
93 
94 /**********************
95  *   STATIC FUNCTIONS
96  **********************/
97 
98 /**
99  * Open a file
100  * @param drv pointer to a driver where this function belongs
101  * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
102  * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
103  * @return pointer to FIL struct or NULL in case of fail
104  */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)105 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
106 {
107     LV_UNUSED(drv);
108 
109     const char * flags = "";
110 
111     if(mode == LV_FS_MODE_WR) flags = "wb";
112     else if(mode == LV_FS_MODE_RD) flags = "rb";
113     else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+";
114 
115     /*Make the path relative to the current directory (the projects root folder)*/
116 
117     char buf[MAX_PATH_LEN];
118     lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
119 
120     return fopen(buf, flags);
121 }
122 
123 /**
124  * Close an opened file
125  * @param drv pointer to a driver where this function belongs
126  * @param file_p pointer to a FILE variable. (opened with fs_open)
127  * @return LV_FS_RES_OK: no error, the file is read
128  *         any error from lv_fs_res_t enum
129  */
fs_close(lv_fs_drv_t * drv,void * file_p)130 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
131 {
132     LV_UNUSED(drv);
133     fclose(file_p);
134     return LV_FS_RES_OK;
135 }
136 
137 /**
138  * Read data from an opened file
139  * @param drv pointer to a driver where this function belongs
140  * @param file_p pointer to a FILE variable.
141  * @param buf pointer to a memory block where to store the read data
142  * @param btr number of Bytes To Read
143  * @param br the real number of read bytes (Byte Read)
144  * @return LV_FS_RES_OK: no error, the file is read
145  *         any error from lv_fs_res_t enum
146  */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)147 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
148 {
149     LV_UNUSED(drv);
150     *br = fread(buf, 1, btr, file_p);
151     return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
152 }
153 
154 /**
155  * Write into a file
156  * @param drv pointer to a driver where this function belongs
157  * @param file_p pointer to a FILE variable
158  * @param buf pointer to a buffer with the bytes to write
159  * @param btw Bytes To Write
160  * @param bw the number of real written bytes (Bytes Written). NULL if unused.
161  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
162  */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)163 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
164 {
165     LV_UNUSED(drv);
166     *bw = fwrite(buf, 1, btw, file_p);
167     return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
168 }
169 
170 /**
171  * Set the read write pointer. Also expand the file size if necessary.
172  * @param drv pointer to a driver where this function belongs
173  * @param file_p pointer to a FILE variable. (opened with fs_open )
174  * @param pos the new position of read write pointer
175  * @return LV_FS_RES_OK: no error, the file is read
176  *         any error from lv_fs_res_t enum
177  */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)178 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
179 {
180     LV_UNUSED(drv);
181     fseek(file_p, pos, whence);
182     return LV_FS_RES_OK;
183 }
184 
185 /**
186  * Give the position of the read write pointer
187  * @param drv pointer to a driver where this function belongs
188  * @param file_p pointer to a FILE variable.
189  * @param pos_p pointer to to store the result
190  * @return LV_FS_RES_OK: no error, the file is read
191  *         any error from lv_fs_res_t enum
192  */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)193 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
194 {
195     LV_UNUSED(drv);
196     *pos_p = ftell(file_p);
197     return LV_FS_RES_OK;
198 }
199 
200 /**
201  * Initialize a 'DIR' or 'HANDLE' variable for directory reading
202  * @param drv pointer to a driver where this function belongs
203  * @param path path to a directory
204  * @return pointer to an initialized 'DIR' or 'HANDLE' variable
205  */
fs_dir_open(lv_fs_drv_t * drv,const char * path)206 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
207 {
208     LV_UNUSED(drv);
209     dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t));
210 #ifndef WIN32
211     /*Make the path relative to the current directory (the projects root folder)*/
212     char buf[MAX_PATH_LEN];
213     lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
214     handle->dir_p = opendir(buf);
215     if(handle->dir_p == NULL) {
216         lv_mem_free(handle);
217         return NULL;
218     }
219     return handle;
220 #else
221     handle->dir_p = INVALID_HANDLE_VALUE;
222     WIN32_FIND_DATAA fdata;
223 
224     /*Make the path relative to the current directory (the projects root folder)*/
225     char buf[MAX_PATH_LEN];
226     lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s\\*", path);
227 
228     strcpy(handle->next_fn, "");
229     handle->dir_p = FindFirstFileA(buf, &fdata);
230     do {
231         if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
232             continue;
233         }
234         else {
235             if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
236                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
237             }
238             else {
239                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
240             }
241             break;
242         }
243     } while(FindNextFileA(handle->dir_p, &fdata));
244 
245     if(handle->dir_p == INVALID_HANDLE_VALUE) {
246         lv_mem_free(handle);
247         return INVALID_HANDLE_VALUE;
248     }
249     return handle;
250 #endif
251 }
252 
253 /**
254  * Read the next filename form a directory.
255  * The name of the directories will begin with '/'
256  * @param drv pointer to a driver where this function belongs
257  * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
258  * @param fn pointer to a buffer to store the filename
259  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
260  */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn)261 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
262 {
263     LV_UNUSED(drv);
264     dir_handle_t * handle = (dir_handle_t *)dir_p;
265 #ifndef WIN32
266     struct dirent * entry;
267     do {
268         entry = readdir(handle->dir_p);
269         if(entry) {
270             if(entry->d_type == DT_DIR) lv_snprintf(fn, MAX_PATH_LEN, "/%s", entry->d_name);
271             else strcpy(fn, entry->d_name);
272         }
273         else {
274             strcpy(fn, "");
275         }
276     } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
277 #else
278     strcpy(fn, handle->next_fn);
279 
280     strcpy(handle->next_fn, "");
281     WIN32_FIND_DATAA fdata;
282 
283     if(FindNextFileA(handle->dir_p, &fdata) == false) return LV_FS_RES_OK;
284     do {
285         if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
286             continue;
287         }
288         else {
289             if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
290                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
291             }
292             else {
293                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
294             }
295             break;
296         }
297     } while(FindNextFileA(handle->dir_p, &fdata));
298 
299 #endif
300     return LV_FS_RES_OK;
301 }
302 
303 /**
304  * Close the directory reading
305  * @param drv pointer to a driver where this function belongs
306  * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
307  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
308  */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)309 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
310 {
311     LV_UNUSED(drv);
312     dir_handle_t * handle = (dir_handle_t *)dir_p;
313 #ifndef WIN32
314     closedir(handle->dir_p);
315 #else
316     FindClose(handle->dir_p);
317 #endif
318     lv_mem_free(handle);
319     return LV_FS_RES_OK;
320 }
321 
322 #else /*LV_USE_FS_STDIO == 0*/
323 
324 #if defined(LV_FS_STDIO_LETTER) && LV_FS_STDIO_LETTER != '\0'
325     #warning "LV_USE_FS_STDIO is not enabled but LV_FS_STDIO_LETTER is set"
326 #endif
327 
328 #endif /*LV_USE_FS_POSIX*/
329 
330