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