]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_stdio.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-Plus-FAT / ff_stdio.c
1 /*\r
2  * FreeRTOS+FAT build 191128 - Note:  FreeRTOS+FAT is still in the lab!\r
3  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  * Authors include James Walmsley, Hein Tibosch and Richard Barry\r
5  *\r
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
7  * this software and associated documentation files (the "Software"), to deal in\r
8  * the Software without restriction, including without limitation the rights to\r
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
10  * the Software, and to permit persons to whom the Software is furnished to do so,\r
11  * subject to the following conditions:\r
12  *\r
13  * The above copyright notice and this permission notice shall be included in all\r
14  * copies or substantial portions of the Software.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * https://www.FreeRTOS.org\r
24  *\r
25  */\r
26 \r
27 /* FreeRTOS includes. */\r
28 #include "FreeRTOS.h"\r
29 #include "task.h"\r
30 #include "portable.h"\r
31 \r
32 /* FreeRTOS+FAT includes. */\r
33 #include "ff_headers.h"\r
34 #include "ff_stdio.h"\r
35 \r
36 #if( ffconfigTIME_SUPPORT != 0 )\r
37         #include <time.h>\r
38 #endif\r
39 \r
40 \r
41 #ifndef SIZE_MAX\r
42         #define SIZE_MAX ( ( size_t ) -1 )\r
43 #endif\r
44 \r
45 /* The number of bytes to write at a time when extending the length of a file\r
46 in a call to ff_truncate(). */\r
47 #define stdioTRUNCATE_WRITE_LENGTH      512\r
48 \r
49 /* Bits set to indicate whether ".." should be included as well as ".". */\r
50 #define stdioDIR_ENTRY_DOT_1            ( 1U & 0x03U )\r
51 #define stdioDIR_ENTRY_DOT_2            ( 2U & 0x03U )\r
52 \r
53 /* The directory entries '.' and '..' will show a file size of 1 KB. */\r
54 #define stdioDOT_ENTRY_FILE_SIZE                        1024\r
55 \r
56 /*-----------------------------------------------------------*/\r
57 \r
58 #if( ffconfigHAS_CWD == 1 )\r
59 \r
60         /* FreeRTOS+FAT requires two thread local storage pointers.  One for errno\r
61         and one for the CWD structure. */\r
62         #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS < 2 )\r
63                 #error FreeRTOS+FAT requires two thread local storage pointers so configNUM_THREAD_LOCAL_STORAGE_POINTERS must be at least 2 in FreeRTOSConfig.h\r
64         #endif\r
65 \r
66         /* Each task has its own Current Working Directory (CWD).  The CWD is used\r
67         to extend relative paths to absolute paths. */\r
68         typedef struct WORKING_DIR\r
69         {\r
70                 char pcCWD[ ffconfigMAX_FILENAME ];             /* The current working directory. */\r
71                 char pcFileName[ ffconfigMAX_FILENAME ];        /* The created absolute path. */\r
72         } WorkingDirectory_t;\r
73 \r
74         /*\r
75          * Add the CWD to the beginning of a relative path, and copy the resultant\r
76          * absolute path into a thread local non const buffer.\r
77          */\r
78         /*static*/ const char *prvABSPath( const char *pcPath );\r
79 \r
80         /*\r
81          * Lookup the CWD of the current task.\r
82          */\r
83         static WorkingDirectory_t *pxFindCWD( void );\r
84 \r
85         /*\r
86          * Convert a string which may contain a relative path into a string that\r
87          * will only contain an absolute path.\r
88          */\r
89         static const char *prvProcessRelativePaths( const char *pcPath );\r
90 \r
91 #else /* ffconfigHAS_CWD */\r
92 \r
93         /* FreeRTOS+FAT requires one thread local storage pointers for errno. */\r
94         #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS < 2 )\r
95                 #error FreeRTOS+FAT requires one thread local storage pointers so configNUM_THREAD_LOCAL_STORAGE_POINTERS must be at least 1 in FreeRTOSConfig.h\r
96         #endif\r
97 \r
98         /* Only absolute paths are supported so define away the prvABSPath()\r
99         function. */\r
100         /*static*/ const char *prvABSPath( const char *pcPath )\r
101         {\r
102                 return pcPath;\r
103         }\r
104 \r
105 #endif /* ffconfigHAS_CWD */\r
106 \r
107 \r
108 #if( ffconfigUSE_DELTREE != 0 )\r
109         /*\r
110          * Remove all files and directories starting from a certain path.\r
111          * This function uses recursion - which breaches the coding standard.  USE\r
112          * WITH CARE.\r
113          */\r
114         static int ff_deltree_recurse( char *pcPath );\r
115 #endif\r
116 \r
117 /*\r
118  * Translate a +FAT error to a value compatible with errno.h\r
119  * If the value represents an error, it is negative\r
120  * The return value of this function will always be positive\r
121  */\r
122 int prvFFErrorToErrno( FF_Error_t xError );\r
123 \r
124 /*\r
125  * Generate a time stamp for the file.\r
126  */\r
127 #if( ffconfigTIME_SUPPORT == 1 )\r
128         static uint32_t prvFileTime( FF_SystemTime_t *pxTime );\r
129 #endif\r
130 \r
131 /*-----------------------------------------------------------*/\r
132 \r
133 FF_FILE *ff_fopen( const char *pcFile, const char *pcMode )\r
134 {\r
135 FF_FILE *pxStream = NULL;\r
136 FF_DirHandler_t xHandler;\r
137 FF_Error_t xError;\r
138 uint8_t ucMode;\r
139 \r
140         /* Insert the current working directory in front of relative paths. */\r
141         pcFile = prvABSPath( pcFile );\r
142 \r
143         /* Look-up the I/O manager for the file system. */\r
144         if( FF_FS_Find( pcFile, &xHandler ) == pdFALSE )\r
145         {\r
146                 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );       /* No such device or address. */\r
147         }\r
148         else\r
149         {\r
150                 /* Now 'xHandler.pcPath' contains an absolute path within the file system.\r
151                 Translate a type string "r|w|a[+]" to +FAT's mode bits. */\r
152                 ucMode = FF_GetModeBits( pcMode );\r
153 \r
154                 pxStream = FF_Open( xHandler.pxManager, xHandler.pcPath, ucMode, &xError );\r
155                 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );\r
156 \r
157                 #if( ffconfigUSE_NOTIFY != 0 )\r
158                 {\r
159                         if( ( pxStream != NULL ) && ( ( ucMode & ( FF_MODE_WRITE | FF_MODE_APPEND ) ) != 0 ) )\r
160                         {\r
161                                 /*_RB_ Function name needs updating. */\r
162                                 callFileEvents( pcFile, eFileCreate );\r
163                         }\r
164                 }\r
165                 #endif  /* ffconfigUSE_NOTIFY */\r
166 \r
167                 #if( ffconfigDEV_SUPPORT != 0 )\r
168                 {\r
169                         if( pxStream != NULL )\r
170                         {\r
171                                 FF_Device_Open( pcFile, pxStream );\r
172                         }\r
173                 }\r
174                 #endif  /* ffconfigDEV_SUPPORT */\r
175         }\r
176 \r
177         return pxStream;\r
178 }\r
179 /*-----------------------------------------------------------*/\r
180 \r
181 int ff_fclose( FF_FILE *pxStream )\r
182 {\r
183 FF_Error_t xError;\r
184 int iReturn, ff_errno;\r
185 \r
186         #if( ffconfigDEV_SUPPORT != 0 )\r
187         {\r
188                 /* Currently device support is in an experimental state.  It will allow\r
189                 to create virtual files. The I/O data to those files will be redirected\r
190                 to their connected "drivers". */\r
191                 if( pxStream != NULL )\r
192                 {\r
193                         FF_Device_Close( pxStream );\r
194                 }\r
195         }\r
196         #endif\r
197 \r
198         xError = FF_Close( pxStream );\r
199         ff_errno = prvFFErrorToErrno( xError );\r
200 \r
201         if( ff_errno == 0 )\r
202         {\r
203                 iReturn = 0;\r
204         }\r
205         else\r
206         {\r
207                 /* Return -1 for error as per normal fclose() semantics. */\r
208                 iReturn = -1;\r
209         }\r
210 \r
211         /* Store the errno to thread local storage. */\r
212         stdioSET_ERRNO( ff_errno );\r
213 \r
214         return iReturn;\r
215 }\r
216 /*-----------------------------------------------------------*/\r
217 \r
218 int ff_fseek( FF_FILE *pxStream, long lOffset, int iWhence )\r
219 {\r
220 FF_Error_t xError;\r
221 int iReturn, ff_errno;\r
222 \r
223 #if( ffconfigDEV_SUPPORT != 0 )\r
224         if( pxStream->pxDevNode != NULL )\r
225         {\r
226                 xError = FF_Device_Seek( pxStream, lOffset, iWhence );\r
227         }\r
228         else\r
229 #endif\r
230         {\r
231                 xError = FF_Seek( pxStream, lOffset, iWhence );\r
232         }\r
233 \r
234         ff_errno = prvFFErrorToErrno( xError );\r
235 \r
236         if( ff_errno == 0 )\r
237         {\r
238                 iReturn = 0;\r
239         }\r
240         else\r
241         {\r
242                 if( xError == FF_ERR_FILE_SEEK_INVALID_POSITION )\r
243                 {\r
244                         /* Illegal position, outside the file's space */\r
245                         ff_errno = pdFREERTOS_ERRNO_ESPIPE;\r
246                 }\r
247                 else if( xError == FF_ERR_FILE_SEEK_INVALID_ORIGIN )\r
248                 {\r
249                         /* Illegal parameter value for iWhence: SET,CUR,END. */\r
250                         ff_errno = pdFREERTOS_ERRNO_EINVAL;\r
251                 }\r
252 \r
253                 /* Return -1 for error as per normal fseek() semantics. */\r
254                 iReturn = -1;\r
255         }\r
256 \r
257         /* Store the errno to thread local storage. */\r
258         stdioSET_ERRNO( ff_errno );\r
259 \r
260         return iReturn;\r
261 }\r
262 /*-----------------------------------------------------------*/\r
263 \r
264 void ff_rewind( FF_FILE *pxStream )\r
265 {\r
266         ff_fseek( pxStream, 0, FF_SEEK_SET );\r
267 \r
268         /* Rewind is supposed to reset errno unconditionally.  Store the errno to\r
269         thread local storage. */\r
270         stdioSET_ERRNO( 0 );\r
271 }\r
272 /*-----------------------------------------------------------*/\r
273 \r
274 long ff_ftell( FF_FILE *pxStream )\r
275 {\r
276 long lResult;\r
277 \r
278         if( pxStream == NULL )\r
279         {\r
280                 /* Store the errno to thread local storage. */\r
281                 stdioSET_ERRNO( pdFREERTOS_ERRNO_EBADF );\r
282 \r
283                 /* Return -1 for error as per normal ftell() semantics. */\r
284                 lResult = -1;\r
285         }\r
286         else\r
287         {\r
288                 lResult = ( long ) pxStream->ulFilePointer;\r
289         }\r
290 \r
291         return lResult;\r
292 }\r
293 /*-----------------------------------------------------------*/\r
294 \r
295 int ff_feof( FF_FILE *pxStream )\r
296 {\r
297 int iResult;\r
298 FF_Error_t xError;\r
299 \r
300         xError = FF_CheckValid( pxStream );\r
301         if( FF_isERR( xError ) == pdFALSE )\r
302         {\r
303                 /* Store the errno to thread local storage. */\r
304                 stdioSET_ERRNO( 0 );\r
305                 if( pxStream->ulFilePointer >= pxStream->ulFileSize )\r
306                 {\r
307                         iResult = pdTRUE;\r
308                 }\r
309                 else\r
310                 {\r
311                         iResult = pdFALSE;\r
312                 }\r
313         }\r
314         else\r
315         {\r
316                 /* Store the errno to thread local storage. */\r
317                 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );\r
318 \r
319                 /* The file was invalid so a non-zero value cannot be returned. */\r
320                 iResult = pdFALSE;\r
321         }\r
322 \r
323         return iResult;\r
324 }\r
325 /*-----------------------------------------------------------*/\r
326 \r
327 size_t ff_fread( void *pvBuffer, size_t xSize, size_t xItems, FF_FILE * pxStream )\r
328 {\r
329 int32_t iReturned;\r
330 size_t xReturn;\r
331 int ff_errno;\r
332 \r
333 #if( ffconfigDEV_SUPPORT != 0 )\r
334         if( pxStream->pxDevNode != NULL )\r
335         {\r
336                 iReturned = FF_Device_Read( pvBuffer, xSize, xItems, pxStream );\r
337         }\r
338         else\r
339 #endif\r
340         {\r
341                 iReturned = FF_Read( pxStream, xSize, xItems, (uint8_t *)pvBuffer );\r
342         }\r
343 \r
344         ff_errno = prvFFErrorToErrno( iReturned );\r
345 \r
346         if( ff_errno == pdFREERTOS_ERRNO_NONE )\r
347         {\r
348                 /* As per the standard fread() semantics, the return value is the number\r
349                 of complete items read, which will only equal the number of bytes\r
350                 transferred when the item size is 1. */\r
351                 xReturn = ( size_t ) iReturned;\r
352         }\r
353         else\r
354         {\r
355                 xReturn = 0;\r
356         }\r
357 \r
358         /* Store the errno to thread local storage. */\r
359         stdioSET_ERRNO( ff_errno );\r
360 \r
361         return xReturn;\r
362 }\r
363 /*-----------------------------------------------------------*/\r
364 \r
365 size_t ff_fwrite( const void *pvBuffer, size_t xSize, size_t xItems, FF_FILE * pxStream )\r
366 {\r
367 int32_t iReturned;\r
368 size_t xReturn;\r
369 int ff_errno;\r
370 \r
371 #if( ffconfigDEV_SUPPORT != 0 )\r
372         if( pxStream->pxDevNode != NULL )\r
373         {\r
374                 iReturned = FF_Device_Write( pvBuffer, xSize, xItems, pxStream );\r
375         }\r
376         else\r
377 #endif\r
378         {\r
379                 iReturned = FF_Write( pxStream, xSize, xItems, (uint8_t *)pvBuffer );\r
380         }\r
381 \r
382         ff_errno = prvFFErrorToErrno( iReturned );\r
383 \r
384         if( ff_errno == pdFREERTOS_ERRNO_NONE )\r
385         {\r
386                 /* As per the standard fwrite() semantics, the return value is the\r
387                 number of complete items read, which will only equal the number of bytes\r
388                 transferred when the item size is 1. */\r
389                 xReturn = ( size_t ) iReturned;\r
390         }\r
391         else\r
392         {\r
393                 xReturn = 0;\r
394         }\r
395 \r
396         /* Store the errno to thread local storage. */\r
397         stdioSET_ERRNO( ff_errno );\r
398 \r
399         return xReturn;\r
400 }\r
401 /*-----------------------------------------------------------*/\r
402 \r
403 int ff_fgetc( FF_FILE * pxStream )\r
404 {\r
405 int32_t iResult;\r
406 int ff_errno;\r
407 \r
408         iResult = FF_GetC( pxStream );\r
409         ff_errno = prvFFErrorToErrno( iResult );\r
410 \r
411         if( ff_errno != 0 )\r
412         {\r
413                 iResult = FF_EOF;\r
414         }\r
415 \r
416         /* Store the errno to thread local storage. */\r
417         stdioSET_ERRNO( ff_errno );\r
418 \r
419         return iResult;\r
420 }\r
421 /*-----------------------------------------------------------*/\r
422 \r
423 int ff_fputc( int iChar, FF_FILE *pxStream )\r
424 {\r
425 int iResult, ff_errno;\r
426 \r
427         iResult = FF_PutC( pxStream, ( uint8_t ) iChar );\r
428         ff_errno = prvFFErrorToErrno( iResult );\r
429 \r
430         if( ff_errno != 0 )\r
431         {\r
432                 iResult = FF_EOF;\r
433         }\r
434 \r
435         /* Store the errno to thread local storage. */\r
436         stdioSET_ERRNO( ff_errno );\r
437 \r
438         return iResult;\r
439 }\r
440 /*-----------------------------------------------------------*/\r
441 \r
442 #if( ffconfigFPRINTF_SUPPORT == 1 )\r
443 \r
444         int ff_fprintf( FF_FILE * pxStream, const char *pcFormat, ... )\r
445         {\r
446         int iCount;\r
447         size_t xResult;\r
448         char *pcBuffer;\r
449         va_list xArgs;\r
450 \r
451                 pcBuffer = ( char * ) ffconfigMALLOC( ffconfigFPRINTF_BUFFER_LENGTH );\r
452                 if( pcBuffer == NULL )\r
453                 {\r
454                         /* Store the errno to thread local storage. */\r
455                         stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );\r
456                         iCount = -1;\r
457                 }\r
458                 else\r
459                 {\r
460                         va_start( xArgs, pcFormat );\r
461                         iCount = vsnprintf( pcBuffer, ffconfigFPRINTF_BUFFER_LENGTH, pcFormat, xArgs );\r
462                         va_end( xArgs );\r
463 \r
464                         /* ff_fwrite() will set ff_errno. */\r
465                         if( iCount > 0 )\r
466                         {\r
467                                 xResult = ff_fwrite( pcBuffer, ( size_t ) 1, ( size_t ) iCount, pxStream );\r
468                                 if( xResult < ( size_t ) iCount )\r
469                                 {\r
470                                         iCount = -1;\r
471                                 }\r
472                         }\r
473 \r
474                         ffconfigFREE( pcBuffer );\r
475                 }\r
476 \r
477                 return iCount;\r
478         }\r
479 \r
480 #endif\r
481 /*-----------------------------------------------------------*/\r
482 \r
483 /*_RB_ to comply with the norm, the second parameter should be an int, but size_t\r
484 is more appropriate. */\r
485 char *ff_fgets( char *pcBuffer, size_t xCount, FF_FILE *pxStream )\r
486 {\r
487 int32_t xResult;\r
488 int ff_errno;\r
489 \r
490         xResult = FF_GetLine( pxStream, ( char * ) pcBuffer, xCount );\r
491 \r
492         /* This call seems to result in errno being incorrectly set to\r
493         FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION when an EOF is encountered. */\r
494         ff_errno = prvFFErrorToErrno( xResult );\r
495 \r
496         if( ff_errno != 0 )\r
497         {\r
498                 pcBuffer = NULL;\r
499         }\r
500 \r
501         /* Store the errno to thread local storage. */\r
502         stdioSET_ERRNO( ff_errno );\r
503 \r
504         return pcBuffer;\r
505 }\r
506 /*-----------------------------------------------------------*/\r
507 \r
508 int ff_seteof( FF_FILE *pxStream )\r
509 {\r
510 FF_Error_t iResult;\r
511 int iReturn, ff_errno;\r
512 \r
513         iResult = FF_SetEof( pxStream );\r
514 \r
515         ff_errno = prvFFErrorToErrno( iResult );\r
516 \r
517         if( ff_errno == 0 )\r
518         {\r
519                 iReturn = 0;\r
520         }\r
521         else\r
522         {\r
523                 iReturn = FF_EOF;\r
524         }\r
525 \r
526         /* Store the errno to thread local storage. */\r
527         stdioSET_ERRNO( ff_errno );\r
528 \r
529         return iReturn;\r
530 }\r
531 /*-----------------------------------------------------------*/\r
532 \r
533 /*_RB_ The norm would be to return an int, but in either case it is not clear\r
534 what state the file is left in (open/closed). */\r
535 FF_FILE *ff_truncate( const char * pcFileName, long lTruncateSize )\r
536 {\r
537 FF_Error_t xResult = 0;\r
538 FF_FILE *pxStream;\r
539 size_t xReturned;\r
540 uint32_t ulLength, ulBytesLeftToAdd, ulBytesToWrite;\r
541 char *pcBufferToWrite;\r
542 \r
543         pxStream = ff_fopen( pcFileName, "a+");\r
544 \r
545         if( pxStream != NULL )\r
546         {\r
547                 ulLength = pxStream->ulFileSize;\r
548         }\r
549         else\r
550         {\r
551                 ulLength = 0;\r
552         }\r
553 \r
554         if( pxStream == NULL )\r
555         {\r
556                 /* Store the errno to thread local storage. */\r
557                 stdioSET_ERRNO( prvFFErrorToErrno( xResult ) );\r
558         }\r
559         else if( ulLength > ( uint32_t ) lTruncateSize )\r
560         {\r
561                 /* Seek the desired position */\r
562                 xResult = FF_Seek( pxStream, lTruncateSize, FF_SEEK_SET );\r
563 \r
564                 /* Make the current position equal to its length */\r
565                 if( FF_isERR( xResult ) == pdFALSE )\r
566                 {\r
567                         xResult = FF_SetEof( pxStream );\r
568                 }\r
569 \r
570                 if( FF_isERR( xResult ) != pdFALSE )\r
571                 {\r
572                         ff_fclose( pxStream );\r
573                         pxStream = NULL;\r
574                 }\r
575 \r
576                 /* Store the errno to thread local storage. */\r
577                 stdioSET_ERRNO( prvFFErrorToErrno( xResult ) );\r
578         }\r
579         else if( ulLength == ( uint32_t ) lTruncateSize )\r
580         {\r
581                 /* Nothing to do, the file has the desired size\r
582                 and the open handle will be returned. */\r
583         }\r
584         else\r
585         {\r
586                 /* lTruncateSize > ulLength.  The user wants to open this file with a\r
587                 larger size than it currently has.  Fill it with zeros. */\r
588                 pcBufferToWrite = ( char * ) ffconfigMALLOC( stdioTRUNCATE_WRITE_LENGTH );\r
589 \r
590                 if( pcBufferToWrite == NULL )\r
591                 {\r
592                         ff_fclose( pxStream );\r
593                         pxStream = NULL;\r
594 \r
595                         /* Store the errno to thread local storage. */\r
596                         stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );\r
597                 }\r
598                 else\r
599                 {\r
600                         /* File has to grow */\r
601                         ulBytesLeftToAdd = ( ( uint32_t ) lTruncateSize ) - ulLength;\r
602 \r
603                         /* Zeros must be written. */\r
604                         memset( pcBufferToWrite, '\0', stdioTRUNCATE_WRITE_LENGTH );\r
605 \r
606                         while( ulBytesLeftToAdd > 0UL )\r
607                         {\r
608                                 if( ( pxStream->ulFileSize % stdioTRUNCATE_WRITE_LENGTH ) != 0 )\r
609                                 {\r
610                                         /* Although +FAT's FF_Write() can handle any size at any\r
611                                         offset, the driver puts data more efficiently if blocks are\r
612                                         written at block boundaries. */\r
613                                         ulBytesToWrite = stdioTRUNCATE_WRITE_LENGTH - ( pxStream->ulFileSize % stdioTRUNCATE_WRITE_LENGTH );\r
614 \r
615                                         if( ulBytesToWrite > ulBytesLeftToAdd )\r
616                                         {\r
617                                                 ulBytesToWrite = ulBytesLeftToAdd;\r
618                                         }\r
619                                 }\r
620                                 else\r
621                                 {\r
622                                         ulBytesToWrite = ulBytesLeftToAdd;\r
623 \r
624                                         if( ulBytesToWrite > stdioTRUNCATE_WRITE_LENGTH )\r
625                                         {\r
626                                                 ulBytesToWrite = stdioTRUNCATE_WRITE_LENGTH;\r
627                                         }\r
628                                 }\r
629 \r
630                                 xReturned = ff_fwrite( pcBufferToWrite, sizeof( char ), ulBytesToWrite, pxStream );\r
631 \r
632                                 if( xReturned != ( size_t ) ulBytesToWrite )\r
633                                 {\r
634                                         /* Write error.  Close the stream and set the proper .\r
635                                         errno. */\r
636                                         ff_fclose( pxStream );\r
637                                         pxStream = NULL;\r
638 \r
639                                         /* Not setting ff_errno because it has been set by other\r
640                                         functions from this ff_stdio. */\r
641 \r
642                                         break;\r
643                                 }\r
644 \r
645                                 ulBytesLeftToAdd -= ulBytesToWrite;\r
646                         }\r
647 \r
648                         ffconfigFREE( pcBufferToWrite );\r
649                 }\r
650         }\r
651 \r
652         return pxStream;\r
653 }\r
654 /*-----------------------------------------------------------*/\r
655 \r
656 #if( ffconfigMKDIR_RECURSIVE == 0 )\r
657 \r
658         /* The normal mkdir() : if assumes that the directories leading to the last\r
659         element of pcDirectory already exists.  For instance: mkdir( "/a/b/c" ) will\r
660         succeed if the path "/a/b" already exists. */\r
661         int ff_mkdir( const char *pcDirectory )\r
662         {\r
663         int iResult, ff_errno;\r
664         FF_DirHandler_t xHandler;\r
665 \r
666                 /* In case a CWD is used, get the absolute path. */\r
667                 pcDirectory = prvABSPath( pcDirectory );\r
668 \r
669                 /* Find the i/o manager for this path */\r
670                 if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )\r
671                 {\r
672                         /* No such device or address. */\r
673                         stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );\r
674                         /* Return -1 for error as per normal mkdir() semantics. */\r
675                         iResult = -1;\r
676                 }\r
677                 else\r
678                 {\r
679                         /* A simple non-recursive make of a directory. */\r
680                         iResult = FF_MkDir( xHandler.pxManager, xHandler.pcPath );\r
681 \r
682                         if( FF_GETERROR( iResult ) == FF_ERR_DIR_OBJECT_EXISTS )\r
683                         {\r
684                                 /* No error if the target directory already exists. */\r
685                                 iResult = FF_ERR_NONE;\r
686                         }\r
687                         ff_errno = prvFFErrorToErrno( iResult );\r
688 \r
689                         /* Store the errno to thread local storage. */\r
690                         stdioSET_ERRNO( ff_errno  );\r
691 \r
692                         if( ff_errno == pdFREERTOS_ERRNO_NONE )\r
693                         {\r
694                                 iResult = 0;\r
695                         }\r
696                         else\r
697                         {\r
698                                 /* Return -1 for error as per normal mkdir() semantics. */\r
699                                 iResult = -1;\r
700                         }\r
701                 }\r
702 \r
703                 return iResult;\r
704         }\r
705 #else   /* ffconfigMKDIR_RECURSIVE */\r
706 \r
707         #warning This path is not yet included in the regression tests.\r
708 \r
709         /* The 'recursive mkdir() : if the parameter 'xRecursive' is non-zero,\r
710         the function will try to create the complete path. */\r
711         int ff_mkdir( const char *pcDirectory, int xRecursive )\r
712         {\r
713         int32_t lResult;\r
714         FF_DirHandler_t xHandler;\r
715 \r
716                 /* In case a CWD is used, get the absolute path. */\r
717                 pcDirectory = prvABSPath( pcDirectory );\r
718 \r
719                 /* Find the i/o manager for this path */\r
720                 if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )\r
721                 {\r
722                         /* No such device or address.  Store the errno to thread local\r
723                         storage. */\r
724                         stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );\r
725                         /* Return -1 for error as per normal mkdir() semantics. */\r
726                         lResult = -1;\r
727                 }\r
728                 else\r
729                 {\r
730                         if( xRecursive == pdFALSE )\r
731                         {\r
732                                 /* A simple non-recursive make of a directory. */\r
733                                 lResult = FF_MkDir( xHandler.pxManager, xHandler.pcPath );\r
734 \r
735                                 if( FF_GETERROR( lResult ) == FF_ERR_DIR_OBJECT_EXISTS )\r
736                                 {\r
737                                         /* No error if the target directory already exists. */\r
738                                         lResult = 0;\r
739                                 }\r
740                         }\r
741                         else\r
742                         {\r
743                                 /* The recursive option is used. */\r
744                                 char pcTempPath[ffconfigMAX_FILENAME];\r
745                                 FF_Error_t errCode;\r
746                                 int iLength = snprintf( pcTempPath, sizeof( pcTempPath ), "%s", xHandler.pcPath );\r
747                                 char *pcPtr = pcTempPath + 1, *pcPrev;\r
748                                 const char *pcLast = pcTempPath + iLength;\r
749 \r
750                                 lResult = FF_ERR_NONE;\r
751 \r
752                                 for( ; ; )\r
753                                 {\r
754                                         for ( pcPrev = pcPtr; pcPtr < pcLast; pcPtr++ )\r
755                                         {\r
756                                                 if( *pcPtr == '/' )\r
757                                                 {\r
758                                                         *pcPtr = '\0';\r
759                                                         break;\r
760                                                 }\r
761                                         }\r
762 \r
763                                         if( pcPrev == pcPtr )\r
764                                         {\r
765                                                 break;\r
766                                         }\r
767 \r
768                                         errCode = FF_MkDir( xHandler.pxManager, pcTempPath );\r
769 \r
770                                         if( FF_isERR( errCode ) && FF_GETERROR( errCode ) != FF_ERR_DIR_OBJECT_EXISTS )\r
771                                         {\r
772                                                 lResult = errCode;\r
773                                                 break;\r
774                                         }\r
775 \r
776                                         if( pcPtr >= ( pcLast - 1 ) )\r
777                                         {\r
778                                                 break;\r
779                                         }\r
780 \r
781                                         *( pcPtr++ ) = '/';\r
782                                 }\r
783                         }\r
784 \r
785                         /* Store the errno to thread local storage. */\r
786                         stdioSET_ERRNO( prvFFErrorToErrno( lResult ) );\r
787                 }\r
788 \r
789                 return lResult;\r
790         }\r
791 #endif /* ffconfigMKDIR_RECURSIVE */\r
792 /*-----------------------------------------------------------*/\r
793 \r
794 int ff_rmdir( const char *pcDirectory )\r
795 {\r
796 int32_t lResult;\r
797 int iReturn, ff_errno;\r
798 FF_DirHandler_t xHandler;\r
799 \r
800         /* In case a CWD is used, get the absolute path */\r
801         pcDirectory = prvABSPath( pcDirectory );\r
802 \r
803         /* Find the i/o manager which can handle this path. */\r
804         if( FF_FS_Find( pcDirectory, &xHandler ) == pdFALSE )\r
805         {\r
806                 ff_errno = pdFREERTOS_ERRNO_ENXIO;      /* No such device or address */\r
807 \r
808                 /* Return -1 for error as per normal rmdir() semantics. */\r
809                 iReturn = -1;\r
810         }\r
811         else\r
812         {\r
813                 lResult = FF_RmDir( xHandler.pxManager, xHandler.pcPath );\r
814                 ff_errno = prvFFErrorToErrno( lResult );\r
815 \r
816                 if( ff_errno == 0 )\r
817                 {\r
818                         iReturn = 0;\r
819                 }\r
820                 else\r
821                 {\r
822                         /* Return -1 for error as per normal rmdir() semantics. */\r
823                         iReturn = -1;\r
824                 }\r
825         }\r
826 \r
827         /* Store the errno to thread local storage. */\r
828         stdioSET_ERRNO( ff_errno );\r
829 \r
830         return iReturn;\r
831 }\r
832 /*-----------------------------------------------------------*/\r
833 \r
834 int ff_remove( const char *pcPath )\r
835 {\r
836 FF_DirHandler_t xHandler;\r
837 FF_Error_t xError;\r
838 int iReturn, ff_errno;\r
839 \r
840         /* In case a CWD is used, get the absolute path */\r
841         pcPath = prvABSPath( pcPath );\r
842 \r
843         /* Find the i/o manager which can handle this path. */\r
844         if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )\r
845         {\r
846                 /* No such device or address */\r
847                 ff_errno = pdFREERTOS_ERRNO_ENXIO;\r
848 \r
849                 /* Return -1 for error as per normal remove() semantics. */\r
850                 iReturn = -1;\r
851         }\r
852         else\r
853         {\r
854                 xError = FF_RmFile( xHandler.pxManager, xHandler.pcPath );\r
855                 ff_errno = prvFFErrorToErrno( xError );\r
856 \r
857                 #if ffconfigUSE_NOTIFY\r
858                 {\r
859                         if( FF_isERR( xError ) == pdFALSE )\r
860                         {\r
861                                 callFileEvents( pcPath, eFileRemove );\r
862                         }\r
863                 }\r
864                 #endif\r
865 \r
866                 if( ff_errno == 0 )\r
867                 {\r
868                         iReturn = 0;\r
869                 }\r
870                 else\r
871                 {\r
872                         /* Return -1 for error as per normal remove() semantics. */\r
873                         iReturn = -1;\r
874                 }\r
875         }\r
876 \r
877         /* Store the errno to thread local storage. */\r
878         stdioSET_ERRNO( ff_errno );\r
879         return iReturn;\r
880 }\r
881 /*-----------------------------------------------------------*/\r
882 /*_RB_ Last parameter not documented. */\r
883 int ff_rename( const char *pcOldName, const char *pcNewName, int bDeleteIfExists )\r
884 {\r
885 FF_DirHandler_t xHandlers[ 2 ];\r
886 FF_Error_t xError = FF_ERR_NONE;\r
887 int ff_errno = 0, iReturn;\r
888 #if( ffconfigHAS_CWD != 0 )\r
889         char *pcOldCopy;\r
890         size_t xSize;\r
891 #endif\r
892 \r
893         /* In case a CWD is used, get the absolute path */\r
894         pcOldName = prvABSPath( pcOldName );\r
895 \r
896         /* Find the i/o manager which can handle this path */\r
897         if( FF_FS_Find( pcOldName, &xHandlers[ 0 ] ) == pdFALSE )\r
898         {\r
899                 xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );\r
900                 ff_errno = pdFREERTOS_ERRNO_ENXIO;      /* No such device or address */\r
901         }\r
902         else\r
903         {\r
904                 #if( ffconfigHAS_CWD != 0 )\r
905                 {\r
906                         xSize = strlen( xHandlers[0].pcPath ) + 1;\r
907                         pcOldCopy = ( char *)ffconfigMALLOC( xSize );\r
908 \r
909                         if( pcOldCopy == NULL )\r
910                         {\r
911                                 /* Could not allocate space to store a file name. */\r
912                                 ff_errno = pdFREERTOS_ERRNO_ENOMEM;\r
913                                 xError = ( int32_t ) ( FF_ERR_NOT_ENOUGH_MEMORY | FF_MOVE );\r
914                         }\r
915                         else\r
916                         {\r
917                                 /* The function prvABSPath() returns a pointer to the task\r
918                                 storage space. Rename needs to call it twice and therefore the\r
919                                 path must be stored before it gets overwritten. */\r
920                                 memcpy( pcOldCopy, xHandlers[0].pcPath, xSize );\r
921                                 xHandlers[0].pcPath = pcOldCopy;\r
922                         }\r
923                 }\r
924                 #endif /* ffconfigHAS_CWD != 0 */\r
925 \r
926 #if( ffconfigHAS_CWD != 0 )\r
927                 if( pcOldCopy != NULL )\r
928 #endif /* ffconfigHAS_CWD != 0 */\r
929                 {\r
930                         pcNewName = prvABSPath( pcNewName );\r
931 \r
932                         /* Find the i/o manager which can handle this path */\r
933                         if( FF_FS_Find( pcNewName, &( xHandlers[ 1 ] ) ) == pdFALSE )\r
934                         {\r
935                                 xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );\r
936                                 ff_errno = pdFREERTOS_ERRNO_ENXIO;      /* No such device or address */\r
937                         }\r
938                         else if( xHandlers[ 0 ].pxManager != xHandlers[ 1 ].pxManager )\r
939                         {\r
940                                 xError = ( int32_t ) ( FF_ERR_NULL_POINTER | FF_MOVE );\r
941                                 /* Cross-device link, which can not be done. */\r
942                                 ff_errno = pdFREERTOS_ERRNO_EXDEV;\r
943                         }\r
944                         else\r
945                         {\r
946                                 xError = FF_Move( xHandlers[ 0 ].pxManager, xHandlers[ 0 ].pcPath, xHandlers[ 1 ].pcPath, bDeleteIfExists );\r
947 \r
948                                 ff_errno = prvFFErrorToErrno( xError );\r
949 \r
950                                 #if ffconfigUSE_NOTIFY\r
951                                 {\r
952                                         if( FF_isERR( xError ) == pdFALSE )\r
953                                         {\r
954                                                 callFileEvents( pcNewName, eFileChange );\r
955                                         }\r
956                                 }\r
957                                 #endif\r
958                         }\r
959 \r
960                         #if( ffconfigHAS_CWD != 0 )\r
961                         {\r
962                                 ffconfigFREE( pcOldCopy );\r
963                         }\r
964                         #endif\r
965                 }\r
966         }\r
967 \r
968         /* Store the errno to thread local storage. */\r
969         stdioSET_ERRNO( ff_errno );\r
970 \r
971         if( ff_errno == 0 )\r
972         {\r
973                 iReturn = 0;\r
974         }\r
975         else\r
976         {\r
977                 /* Return -1 for error as per normal rmdir() semantics. */\r
978                 iReturn = -1;\r
979         }\r
980 \r
981         return iReturn;\r
982 }\r
983 /*-----------------------------------------------------------*/\r
984 \r
985 int ff_stat( const char *pcName, FF_Stat_t *pxStatBuffer )\r
986 {\r
987 FF_DirEnt_t xDirEntry;\r
988 uint32_t ulFileCluster;\r
989 FF_Error_t xError;\r
990 int iResult;\r
991 FF_DirHandler_t xHandler;\r
992 BaseType_t xIndex;\r
993 FF_FindParams_t xFindParams;\r
994 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )\r
995         const FF_T_WCHAR *pcFileName = NULL;\r
996 #else\r
997         /* Initialised to prevent MSVC incorrectly claiming the variable is used\r
998         without being initialised. */\r
999         const char *pcFileName = NULL;\r
1000 #endif\r
1001 \r
1002         memset( &xFindParams, '\0', sizeof( xFindParams ) );\r
1003 \r
1004         /* Insert the current working directory in front of relative paths. */\r
1005         pcName = prvABSPath( pcName );\r
1006 \r
1007         /* Look-up the I/O manager for the file system. */\r
1008         if( FF_FS_Find( pcName, &xHandler ) == pdFALSE )\r
1009         {\r
1010                 /* No such device or address. */\r
1011                 xError = ( FF_Error_t ) ( pdFREERTOS_ERRNO_ENXIO | FF_STAT_FUNC );\r
1012         }\r
1013         else\r
1014         {\r
1015                 xError = FF_ERR_NONE;\r
1016                 pcName = xHandler.pcPath;\r
1017 \r
1018                 /* Let xIndex point to the last occurrence of '/' or '\', to separate\r
1019                 the path from the file name. */\r
1020                 xIndex = ( BaseType_t ) STRLEN( pcName );\r
1021                 while( xIndex != 0 )\r
1022                 {\r
1023                         if( ( pcName[ xIndex ] == '\\' ) || ( pcName[ xIndex ] == '/' ) )\r
1024                         {\r
1025                                 break;\r
1026                         }\r
1027 \r
1028                         xIndex--;\r
1029                 }\r
1030 \r
1031                 /* Copy the file name, i.e. the string that comes after the last\r
1032                 separator. */\r
1033                 pcFileName = pcName + xIndex + 1;\r
1034 \r
1035                 if( xIndex == 0 )\r
1036                 {\r
1037                         /* Only for the root, the slash is part of the directory name.\r
1038                         'xIndex' now equals to the length of the path name. */\r
1039                         xIndex = 1;\r
1040                 }\r
1041 \r
1042                 /* FF_CreateShortName() might set flags FIND_FLAG_FITS_SHORT and\r
1043                 FIND_FLAG_SIZE_OK. */\r
1044                 FF_CreateShortName( &xFindParams, pcFileName );\r
1045 \r
1046                 /* Lookup the path and find the cluster pointing to the directory: */\r
1047                 xFindParams.ulDirCluster = FF_FindDir( xHandler.pxManager, pcName, xIndex, &xError );\r
1048         }\r
1049 \r
1050         if( FF_isERR( xError ) == pdFALSE )\r
1051         {\r
1052                 /* See if the file does exist within the given directory. */\r
1053                 ulFileCluster = FF_FindEntryInDir( xHandler.pxManager, &xFindParams, pcFileName, 0x00, &xDirEntry, &xError );\r
1054 \r
1055                 if( ulFileCluster == 0ul )\r
1056                 {\r
1057                         /* If cluster 0 was returned, it might be because the file has no allocated cluster,\r
1058                         i.e. only a directory entry and no stored data. */\r
1059                         if( STRLEN( pcFileName ) == STRLEN( xDirEntry.pcFileName ) )\r
1060                         {\r
1061                                 if( ( xDirEntry.ulFileSize == 0 ) && ( FF_strmatch( pcFileName, xDirEntry.pcFileName, ( BaseType_t ) STRLEN( pcFileName ) ) == pdTRUE ) )\r
1062                                 {\r
1063                                         /* It is the file, give it a pseudo cluster number '1'. */\r
1064                                         ulFileCluster = 1;\r
1065                                         /* And reset any error. */\r
1066                                         xError = FF_ERR_NONE;\r
1067                                 }\r
1068                         }\r
1069                 }\r
1070 \r
1071                 /* Test 'ulFileCluster' again, it might have been changed. */\r
1072                 if( ulFileCluster == 0ul )\r
1073                 {\r
1074                         xError = FF_ERR_FILE_NOT_FOUND | FF_STAT_FUNC;\r
1075                 }\r
1076         }\r
1077 \r
1078         if( ( pxStatBuffer != NULL ) && ( FF_isERR( xError ) == pdFALSE ) )\r
1079         {\r
1080                 if( ( xDirEntry.ucAttrib & FF_FAT_ATTR_DIR ) != 0 )\r
1081                 {\r
1082                         pxStatBuffer->st_mode = ( unsigned short ) FF_IFDIR;\r
1083                 }\r
1084                 else\r
1085                 {\r
1086                         pxStatBuffer->st_mode = ( unsigned short ) FF_IFREG;\r
1087                 }\r
1088 \r
1089                 #if( ffconfigDEV_SUPPORT != 0 )\r
1090                 {\r
1091                 BaseType_t bIsDeviceDir = xCheckDevicePath( pcFileName );\r
1092 \r
1093                         if( bIsDeviceDir != pdFALSE )\r
1094                         {\r
1095                                 FF_Device_GetDirEnt( xHandler.pcPath, &( xDirEntry ) );\r
1096                         }\r
1097                 }\r
1098                 #endif\r
1099 \r
1100                 /* Despite the warning output by MSVC - it is not possible to get here\r
1101                 if xDirEntry has not been initialised. */\r
1102                 pxStatBuffer->st_size = xDirEntry.ulFileSize;\r
1103                 pxStatBuffer->st_ino = xDirEntry.ulObjectCluster;\r
1104                 pxStatBuffer->st_dev = ( short ) xHandler.xFSIndex;\r
1105 \r
1106                 #if( ffconfigTIME_SUPPORT == 1 )\r
1107                 {\r
1108                         pxStatBuffer->st_atime = ( unsigned long ) prvFileTime( &( xDirEntry.xAccessedTime ) );\r
1109                         pxStatBuffer->st_mtime = ( unsigned long ) prvFileTime( &( xDirEntry.xModifiedTime ) );\r
1110                         pxStatBuffer->st_ctime = ( unsigned long ) prvFileTime( &( xDirEntry.xCreateTime ) );\r
1111                 }\r
1112                 #endif\r
1113         }\r
1114 \r
1115         stdioSET_ERRNO( prvFFErrorToErrno( xError ) );\r
1116 \r
1117         if( FF_isERR( xError ) == pdFALSE )\r
1118         {\r
1119                 iResult = 0;\r
1120         }\r
1121         else\r
1122         {\r
1123                 iResult = -1;\r
1124         }\r
1125 \r
1126         return iResult;\r
1127 }  /* ff_stat() */\r
1128 /*-----------------------------------------------------------*/\r
1129 \r
1130 #if(  ffconfigHAS_CWD == 1 )\r
1131 \r
1132         int ff_chdir( const char *pcDirectoryName )\r
1133         {\r
1134         int iResult, iLength, iValid = pdFALSE;\r
1135         WorkingDirectory_t *pxDir = NULL;\r
1136 \r
1137                 /* Not all paths set an errno. */\r
1138                 stdioSET_ERRNO( 0 );\r
1139 \r
1140                 /* Is there a file system mounted? */\r
1141                 if( FF_FS_Count() != 0 )\r
1142                 {\r
1143                         /* In case a CWD is used, get the absolute path. */\r
1144                         pcDirectoryName = prvABSPath( pcDirectoryName );\r
1145                         pxDir = pxFindCWD();\r
1146 \r
1147                         if( pxDir == NULL )\r
1148                         {\r
1149                                 /* Store the errno to thread local storage. */\r
1150                                 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );\r
1151 \r
1152                                 /* Return -1 for error as per normal chdir() semantics. */\r
1153                                 iResult = -1;\r
1154                         }\r
1155                         else\r
1156                         {\r
1157                                 /* The CWD will be stored without a trailing '/'.  If "/"\r
1158                                 happens to be the CWD, it will be stored as an empty string. */\r
1159                                 iLength = strlen( pcDirectoryName );\r
1160 \r
1161                                 /* Knock off the trailing / if one exits - being careful not to\r
1162                                 remove the trailing slash if this is the root directory. */\r
1163                                 if( ( iLength > 1 ) && ( pxDir->pcFileName[ iLength - 1 ] == '/' ) )\r
1164                                 {\r
1165                                         pxDir->pcFileName[ iLength - 1 ] = '\0';\r
1166                                 }\r
1167 \r
1168                                 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOENT );\r
1169 \r
1170                                 /* Does the directory exist? */\r
1171                                 if( strcmp( pcDirectoryName, "/" ) == 0 )\r
1172                                 {\r
1173                                         /* Moving to the root - which exists. */\r
1174                                         iValid = pdTRUE;\r
1175                                 }\r
1176                                 else if( ff_finddir( pxDir->pcFileName ) != pdFALSE )\r
1177                                 {\r
1178                                         iValid = pdTRUE;\r
1179                                 }\r
1180                         }\r
1181                 }\r
1182 \r
1183                 if( iValid == pdTRUE )\r
1184                 {\r
1185                         /* The generated name becomes the CWD.  No need to test for overflow\r
1186                         as pcPath and pcFileName are the same size. */\r
1187                         strcpy( pxDir->pcCWD, pxDir->pcFileName );\r
1188 \r
1189                         /* chdir returns 0 for success. */\r
1190                         iResult = FF_ERR_NONE;\r
1191                 }\r
1192                 else\r
1193                 {\r
1194                         /* Return -1 for error as per normal chdir() semantics. */\r
1195                         iResult = -1;\r
1196                 }\r
1197 \r
1198                 return iResult;\r
1199         }\r
1200 \r
1201 #endif /* ffconfigHAS_CWD == 1 */\r
1202 /*-----------------------------------------------------------*/\r
1203 \r
1204 #if(  ffconfigHAS_CWD == 1 )\r
1205 \r
1206         char *ff_getcwd( char *pcBuffer, size_t xBufferLength )\r
1207         {\r
1208         WorkingDirectory_t *pxDir = pxFindCWD();\r
1209 \r
1210                 stdioSET_ERRNO( 0 );\r
1211 \r
1212                 if( ( pxDir == NULL ) || ( pxDir->pcCWD[0] == '\0' ) )\r
1213                 {\r
1214                         if( xBufferLength > strlen( "/" ) )\r
1215                         {\r
1216                                 strncpy( pcBuffer, "/", xBufferLength );\r
1217                         }\r
1218                         else\r
1219                         {\r
1220                                 pcBuffer = NULL;\r
1221                         }\r
1222                 }\r
1223                 else\r
1224                 {\r
1225                         if( strlen( pxDir->pcCWD ) < xBufferLength )\r
1226                         {\r
1227                                 strncpy( pcBuffer, pxDir->pcCWD, xBufferLength );\r
1228                         }\r
1229                         else\r
1230                         {\r
1231                                 pcBuffer = NULL;\r
1232                         }\r
1233                 }\r
1234 \r
1235                 return pcBuffer;\r
1236         }\r
1237 \r
1238 #endif  /* ffconfigHAS_CWD */\r
1239 /*-----------------------------------------------------------*/\r
1240 \r
1241 int ff_findfirst( const char *pcPath, FF_FindData_t *pxFindData )\r
1242 {\r
1243 int iIsRootDir, iReturn;\r
1244 const char *pcDirectory;\r
1245 \r
1246         iReturn = 0;\r
1247 \r
1248         memset( pxFindData, '\0', sizeof( *pxFindData ) );\r
1249 \r
1250         pxFindData->pcFileName = pxFindData->xDirectoryEntry.pcFileName;\r
1251 \r
1252         /* In case a CWD is used, get the absolute path. */\r
1253         pcDirectory = prvABSPath( pcPath );\r
1254 \r
1255         if( ( pcDirectory[ 0 ] == '/' ) && ( pcDirectory[ 1 ] == 0x00 ) )\r
1256         {\r
1257                 iIsRootDir = pdTRUE;\r
1258         }\r
1259         else\r
1260         {\r
1261                 iIsRootDir = pdFALSE;\r
1262         }\r
1263 \r
1264                 /* Find the i/o manager that can handle this path. */\r
1265         if( FF_FS_Find( pcDirectory, &( pxFindData->xDirectoryHandler ) ) == pdFALSE )\r
1266         {\r
1267                 if( ( iIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) )\r
1268                 {\r
1269                         stdioSET_ERRNO( prvFFErrorToErrno( ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDFIRST ) ) );\r
1270                         iReturn = -1;\r
1271                 }\r
1272         }\r
1273 \r
1274         /* Check no errors before continuing. */\r
1275         if( iReturn == 0 )\r
1276         {\r
1277                 #if( ffconfigDEV_SUPPORT != 0 )\r
1278                 {\r
1279                         pxFindData->bIsDeviceDir = xCheckDevicePath( pcDirectory );\r
1280                 }\r
1281                 #endif\r
1282 \r
1283                 if( iIsRootDir != pdFALSE )\r
1284                 {\r
1285                         /* A listing of the root directory will include pseudo entries\r
1286                         such as /ram /nand. */\r
1287                         pxFindData->xDirectoryHandler.xFSIndex = FF_FS_Count();\r
1288 \r
1289                         /* Only add '.' */\r
1290                         pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = stdioDIR_ENTRY_DOT_1;\r
1291                 }\r
1292                 else\r
1293                 {\r
1294                         /* This is the root of a sub file system, add "." and ".." */\r
1295                         pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = stdioDIR_ENTRY_DOT_1 | stdioDIR_ENTRY_DOT_2;\r
1296                 }\r
1297 \r
1298                 pxFindData->xDirectoryHandler.u.bits.bIsValid = pdTRUE;\r
1299                 iReturn = ff_findnext( pxFindData );\r
1300         }\r
1301         else\r
1302         {\r
1303                 /* errno has already been set. */\r
1304         }\r
1305 \r
1306         return iReturn;\r
1307 }\r
1308 /*-----------------------------------------------------------*/\r
1309 \r
1310 int ff_findnext( FF_FindData_t *pxFindData )\r
1311 {\r
1312 FF_Error_t xError;\r
1313 #if( ffconfigTIME_SUPPORT != 0 )\r
1314         BaseType_t xSetTime = 0;\r
1315 #endif  /* ffconfigTIME_SUPPORT */\r
1316 \r
1317 \r
1318         if( pxFindData->xDirectoryHandler.u.bits.bIsValid == pdFALSE )\r
1319         {\r
1320                 xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PARAMETER | FF_FINDNEXT );\r
1321                 FF_PRINTF("ff_findnext: xDirectoryHandler not valid\n" );\r
1322         }\r
1323         else\r
1324         {\r
1325                 xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );\r
1326                 if( pxFindData->xDirectoryHandler.pxManager != NULL )\r
1327                 {\r
1328                         if( pxFindData->xDirectoryHandler.u.bits.bFirstCalled == pdFALSE )\r
1329                         {\r
1330                                 pxFindData->xDirectoryHandler.u.bits.bFirstCalled = pdTRUE;\r
1331                                 xError = FF_FindFirst( pxFindData->xDirectoryHandler.pxManager, &( pxFindData->xDirectoryEntry ),\r
1332                                         pxFindData->xDirectoryHandler.pcPath );\r
1333                         }\r
1334                         else if( pxFindData->xDirectoryHandler.u.bits.bEndOfDir == pdFALSE )\r
1335                         {\r
1336                                 xError = FF_FindNext( pxFindData->xDirectoryHandler.pxManager, &( pxFindData->xDirectoryEntry ) );\r
1337                         }\r
1338 \r
1339                         if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )\r
1340                         {\r
1341                                 /* Stop further calls to FF_FindNext(). */\r
1342                                 pxFindData->xDirectoryHandler.u.bits.bEndOfDir = pdTRUE;\r
1343                         }\r
1344 \r
1345                         #if( ffconfigDEV_SUPPORT != 0 )\r
1346                         {\r
1347                                 if( pxFindData->bIsDeviceDir != pdFALSE )\r
1348                                 {\r
1349                                         FF_Device_GetDirEnt( pxFindData->xDirectoryHandler.pcPath, &( pxFindData->xDirectoryEntry ) );\r
1350                                 }\r
1351                         }\r
1352                         #endif\r
1353                 }\r
1354 \r
1355                 if( FF_isERR( xError ) == pdFALSE )\r
1356                 {\r
1357                         /* If an entry is found, see if it is a dot-entry.  Dot-entries\r
1358                         ("." and "..") need a time-stamp. */\r
1359                         if( pxFindData->xDirectoryEntry.pcFileName[ 0 ] == '.' )\r
1360                         {\r
1361                                 if( ( pxFindData->xDirectoryEntry.pcFileName[ 1 ] == '.' ) &&\r
1362                                         ( pxFindData->xDirectoryEntry.pcFileName[ 2 ] == '\0' ) )\r
1363                                 {\r
1364                                         /* This is a directory "..". Clear the flag for DOT_2. */\r
1365                                         pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_1;\r
1366                                         #if( ffconfigTIME_SUPPORT != 0 )\r
1367                                         {\r
1368                                                 /* The dot-entries do not have a proper time stamp, add\r
1369                                                 it here. */\r
1370                                                 xSetTime = pdTRUE;\r
1371                                         }\r
1372                                         #endif  /* ffconfigTIME_SUPPORT */\r
1373                                 }\r
1374                                 else if( pxFindData->xDirectoryEntry.pcFileName[ 1 ] == '\0' )\r
1375                                 {\r
1376                                         /* This is a directory ".". Clear the flag for DOT_1. */\r
1377                                         pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_2;\r
1378                                         #if( ffconfigTIME_SUPPORT != 0 )\r
1379                                         {\r
1380                                                 xSetTime = pdTRUE;\r
1381                                         }\r
1382                                         #endif  /* ffconfigTIME_SUPPORT */\r
1383                                 }\r
1384                         }\r
1385                 }\r
1386 \r
1387                 if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )\r
1388                 {\r
1389                         /* No more physical entries were found.  Now see if there are FS\r
1390                         entries or dot-entries to be added: */\r
1391                         while( ( pxFindData->xDirectoryHandler.xFSIndex > 0 ) ||\r
1392                                    ( pxFindData->xDirectoryHandler.u.bits.bAddDotEntries != 0 ) )\r
1393                         {\r
1394                                 if( pxFindData->xDirectoryHandler.xFSIndex > 0 )\r
1395                                 {\r
1396                                 FF_SubSystem_t xSubSystem;\r
1397                                 int found;\r
1398 \r
1399                                         pxFindData->xDirectoryHandler.xFSIndex--;\r
1400                                         found = FF_FS_Get( pxFindData->xDirectoryHandler.xFSIndex, &xSubSystem );\r
1401 \r
1402                                         if( ( found == pdFALSE ) || ( xSubSystem.pcPath[ 1 ] == '\0' ) )\r
1403                                         {\r
1404                                                 continue;\r
1405                                         }\r
1406                                         snprintf( pxFindData->xDirectoryEntry.pcFileName, sizeof( pxFindData->xDirectoryEntry.pcFileName ), "%s", xSubSystem.pcPath + 1 );\r
1407 \r
1408                                         if( xSubSystem.pxManager != NULL )\r
1409                                         {\r
1410                                                 pxFindData->xDirectoryEntry.ulObjectCluster = xSubSystem.pxManager->xPartition.ulRootDirCluster;\r
1411                                         }\r
1412                                         else\r
1413                                         {\r
1414                                                 pxFindData->xDirectoryEntry.ulObjectCluster = 0;\r
1415                                         }\r
1416                                 }\r
1417                                 else if( ( pxFindData->xDirectoryHandler.u.bits.bAddDotEntries & stdioDIR_ENTRY_DOT_2 ) != 0 )\r
1418                                 {\r
1419                                         strcpy( pxFindData->xDirectoryEntry.pcFileName, "..");\r
1420 \r
1421                                         /* Clear DOT_2 (keep DOT_1). */\r
1422                                         pxFindData->xDirectoryHandler.u.bits.bAddDotEntries &= stdioDIR_ENTRY_DOT_1;\r
1423                                 }\r
1424                                 else\r
1425                                 {\r
1426                                         strcpy( pxFindData->xDirectoryEntry.pcFileName, ".");\r
1427                                         pxFindData->xDirectoryHandler.u.bits.bAddDotEntries = 0;\r
1428                                 }\r
1429 \r
1430                                 pxFindData->xDirectoryEntry.ucAttrib = FF_FAT_ATTR_READONLY | FF_FAT_ATTR_DIR;\r
1431                                 pxFindData->xDirectoryEntry.ulFileSize = stdioDOT_ENTRY_FILE_SIZE;\r
1432                                 #if( ffconfigTIME_SUPPORT != 0 )\r
1433                                 {\r
1434                                         xSetTime = pdTRUE;\r
1435                                 }\r
1436                                 #endif  /* ffconfigTIME_SUPPORT */\r
1437 \r
1438                                 xError = FF_ERR_NONE;\r
1439                                 break;\r
1440                         }\r
1441                 }\r
1442 \r
1443                 #if( ffconfigTIME_SUPPORT != 0 )\r
1444                 {\r
1445                         if( xSetTime != pdFALSE )\r
1446                         {\r
1447                                 FF_TimeStruct_t xTimeStruct;\r
1448                                 time_t xSeconds;\r
1449 \r
1450                                 xSeconds = FreeRTOS_time( NULL );\r
1451                                 FreeRTOS_gmtime_r( &xSeconds, &xTimeStruct );\r
1452 \r
1453                                 pxFindData->xDirectoryEntry.xCreateTime.Year   = ( uint16_t ) ( xTimeStruct.tm_year + 1900 );   /* Year (e.g. 2009). */\r
1454                                 pxFindData->xDirectoryEntry.xCreateTime.Month  = ( uint16_t ) ( xTimeStruct.tm_mon + 1 );               /* Month (e.g. 1 = Jan, 12 = Dec). */\r
1455                                 pxFindData->xDirectoryEntry.xCreateTime.Day    = ( uint16_t ) xTimeStruct.tm_mday;                              /* Day (1 - 31). */\r
1456                                 pxFindData->xDirectoryEntry.xCreateTime.Hour   = ( uint16_t ) xTimeStruct.tm_hour;                              /* Hour (0 - 23). */\r
1457                                 pxFindData->xDirectoryEntry.xCreateTime.Minute = ( uint16_t ) xTimeStruct.tm_min;                               /* Min (0 - 59). */\r
1458                                 pxFindData->xDirectoryEntry.xCreateTime.Second = ( uint16_t ) xTimeStruct.tm_sec;                               /* Second (0 - 59). */\r
1459                                 pxFindData->xDirectoryEntry.xModifiedTime      = pxFindData->xDirectoryEntry.xCreateTime;               /* Date and Time Modified. */\r
1460                                 pxFindData->xDirectoryEntry.xAccessedTime      = pxFindData->xDirectoryEntry.xCreateTime;               /* Date of Last Access. */\r
1461                         }\r
1462                 }\r
1463                 #endif  /* ffconfigTIME_SUPPORT */\r
1464 \r
1465                 if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )\r
1466                 {\r
1467                         /* FF_ERR_DIR_END_OF_DIR will be returned. */\r
1468                         pxFindData->xDirectoryHandler.u.bits.bIsValid = 0;\r
1469                 }\r
1470 \r
1471                 pxFindData->ucAttributes = pxFindData->xDirectoryEntry.ucAttrib;\r
1472                 pxFindData->ulFileSize = pxFindData->xDirectoryEntry.ulFileSize;\r
1473         }\r
1474 \r
1475         stdioSET_ERRNO( prvFFErrorToErrno( xError ) );\r
1476 \r
1477         return xError;\r
1478 }\r
1479 /*-----------------------------------------------------------*/\r
1480 \r
1481 /*-----------------------------------------------------------\r
1482  * ff_isdirempty() returns 1 if a given directory is empty\r
1483  * (has no entries)\r
1484  *-----------------------------------------------------------*/\r
1485 int ff_isdirempty(const char *pcPath )\r
1486 {\r
1487         FF_DirHandler_t xHandler;\r
1488         int iResult;\r
1489         /* In case a CWD is used, get the absolute path */\r
1490         pcPath = prvABSPath( pcPath );\r
1491         /* Find the i/o manager which can handle this path */\r
1492         if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )\r
1493         {\r
1494                 iResult = ( int ) ( FF_ERR_NULL_POINTER | FF_ISDIREMPTY );\r
1495         }\r
1496         else\r
1497         {\r
1498                 iResult = FF_isDirEmpty( xHandler.pxManager, xHandler.pcPath );\r
1499         }\r
1500 \r
1501         /* Store the errno to thread local storage. */\r
1502         stdioSET_ERRNO( prvFFErrorToErrno( iResult ) );\r
1503         return iResult;\r
1504 }\r
1505 /*-----------------------------------------------------------*/\r
1506 \r
1507 #if (ffconfig64_NUM_SUPPORT != 0 )\r
1508 int64_t ff_diskfree(const char *pcPath, uint32_t *pxSectorCount )\r
1509 #else\r
1510 int32_t ff_diskfree(const char *pcPath, uint32_t *pxSectorCount )\r
1511 #endif\r
1512 {\r
1513 FF_DirHandler_t xHandler;\r
1514 FF_Error_t xError;\r
1515 #if (ffconfig64_NUM_SUPPORT != 0 )\r
1516         #define DISKFREE_RETURN_TYPE    int64_t\r
1517         int64_t lReturn;\r
1518 #else\r
1519         #define DISKFREE_RETURN_TYPE    int32_t\r
1520         int32_t lReturn;\r
1521 #endif\r
1522 \r
1523         if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )\r
1524         {\r
1525                 /* Return cluster 0 for error. */\r
1526                 lReturn = 0ul;\r
1527 \r
1528                 /* Store the errno to thread local storage. */\r
1529                 stdioSET_ERRNO( pdFREERTOS_ERRNO_ENXIO );       /* No such device or address */\r
1530         }\r
1531         else\r
1532         {\r
1533                 if (pxSectorCount != NULL )\r
1534                 {\r
1535                         *pxSectorCount = xHandler.pxManager->xPartition.ulDataSectors;\r
1536                 }\r
1537 \r
1538                 lReturn = ( DISKFREE_RETURN_TYPE ) FF_GetFreeSize( xHandler.pxManager, &xError ) / 512;\r
1539 \r
1540                 /* Store the errno to thread local storage. */\r
1541                 stdioSET_ERRNO( prvFFErrorToErrno( xError ) );\r
1542         }\r
1543 \r
1544         return lReturn;\r
1545 }\r
1546 /*-----------------------------------------------------------*/\r
1547 \r
1548 int ff_finddir(const char *pcPath )\r
1549 {\r
1550 int iResult;\r
1551 FF_DirHandler_t xHandler;\r
1552 FF_Error_t errCode;\r
1553 \r
1554         if( FF_FS_Find( pcPath, &xHandler ) == pdFALSE )\r
1555         {\r
1556                 /* Return cluster 0 for error. */\r
1557                 iResult = 0;\r
1558         }\r
1559         else\r
1560         {\r
1561                 iResult = ( int ) FF_FindDir( xHandler.pxManager, xHandler.pcPath, ( uint16_t ) strlen( xHandler.pcPath ), &errCode );\r
1562         }\r
1563 \r
1564         return iResult;\r
1565 }\r
1566 /*-----------------------------------------------------------*/\r
1567 \r
1568 size_t ff_filelength( FF_FILE *pxStream )\r
1569 {\r
1570 FF_Error_t xReturned;\r
1571 uint32_t ulLength;\r
1572 \r
1573         xReturned = FF_GetFileSize( pxStream, &( ulLength ) );\r
1574 \r
1575         if( FF_isERR( xReturned ) != pdFALSE )\r
1576         {\r
1577                 /* An error. */\r
1578                 ulLength = ( uint32_t ) 0u;\r
1579                 stdioSET_ERRNO( prvFFErrorToErrno( xReturned ) );\r
1580         }\r
1581         else\r
1582         {\r
1583                 stdioSET_ERRNO( pdFREERTOS_ERRNO_NONE );\r
1584         }\r
1585 \r
1586         return ( size_t ) ulLength;\r
1587 }\r
1588 /*-----------------------------------------------------------*/\r
1589 \r
1590 /*-----------------------------------------------------------\r
1591  * Delete a directory and, recursively, all of its contents\r
1592  *-----------------------------------------------------------*/\r
1593 #if( ffconfigUSE_DELTREE != 0 )\r
1594         int ff_deltree( const char *pcDirectory )\r
1595         {\r
1596         int iResult;\r
1597         char *pcPath;\r
1598 \r
1599                 pcPath = ( char * ) ffconfigMALLOC( ffconfigMAX_FILENAME );\r
1600                 if( pcPath != NULL )\r
1601                 {\r
1602                         /* In case a CWD is used, get the absolute path */\r
1603                         pcDirectory = prvABSPath( pcDirectory );\r
1604                         snprintf (pcPath, ffconfigMAX_FILENAME, "%s", pcDirectory);\r
1605                         /* This recursive function will do all the work */\r
1606                         iResult = ff_deltree_recurse (pcPath);\r
1607                         if( iResult >= 0 )\r
1608                         {\r
1609                                 iResult = ff_rmdir( pcPath );\r
1610                                 if( iResult )\r
1611                                 {\r
1612                                         FF_PRINTF("ff_deltree(%s): %s\n", pcPath, strerror( stdioGET_ERRNO( ) ) );\r
1613                                 }\r
1614                         }\r
1615                         ffconfigFREE( pcPath );\r
1616                 }\r
1617                 else\r
1618                 {\r
1619                         iResult = -1;\r
1620                         stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );\r
1621                 }\r
1622                 return iResult;\r
1623         }\r
1624 #endif /* ffconfigUSE_DELTREE */\r
1625 /*-----------------------------------------------------------*/\r
1626 \r
1627 #if( ffconfigUSE_DELTREE != 0 )\r
1628         static int ff_deltree_recurse( char *pcPath )\r
1629         {\r
1630         FF_FindData_t *pxFindData;\r
1631         BaseType_t xIsDir, xIsDotDir;\r
1632         FF_Error_t xError;\r
1633         int iResult, iNext, iNameLength, pass, iCount = 0;\r
1634 \r
1635                 pxFindData = ( FF_FindData_t * ) ffconfigMALLOC( sizeof( *pxFindData ) );\r
1636                 if( pxFindData != NULL )\r
1637                 {\r
1638                         iNameLength = ( int ) strlen( pcPath );\r
1639                         /* The directory will be scanned 2 times.  First the sub-directories will be\r
1640                         entered and their contents deleted.  In the second pass the files in the\r
1641                         current directory will be removed.  In this way 'pcPath' can be constantly\r
1642                         used and reused recursively which is cheaper than allocating 'ffconfigMAX_FILENAME'\r
1643                         bytes within each recursion. */\r
1644                         for( pass = 0; pass < 2; pass++ )\r
1645                         {\r
1646                                 for( iResult = ff_findfirst( pcPath, pxFindData );\r
1647                                          iResult == 0;\r
1648                                          iResult = iNext )\r
1649                                 {\r
1650                                         xIsDir = ( pxFindData->xDirectoryEntry.ucAttrib & FF_FAT_ATTR_DIR ) != 0;\r
1651                                         if( ( pass == 0 ) && ( xIsDir != pdFALSE ) )\r
1652                                         {\r
1653                                         /* This entry is a directory.  Don't traverse '.' or '..' */\r
1654                                         xIsDotDir = 0;\r
1655 \r
1656                                                 if( pxFindData->pcFileName[ 0 ] == '.' )\r
1657                                                 {\r
1658                                                         if( ( pxFindData->pcFileName[ 1 ] == '.' ) &&\r
1659                                                                 ( pxFindData->pcFileName[ 2 ] == '\0' ) )\r
1660                                                         {\r
1661                                                                 xIsDotDir = 2;\r
1662                                                         }\r
1663                                                         else if( pxFindData->pcFileName[ 1 ] == '\0' )\r
1664                                                         {\r
1665                                                                 xIsDotDir = 1;\r
1666                                                         }\r
1667                                                 }\r
1668                                                 if( xIsDotDir == 0 )\r
1669                                                 {\r
1670                                                         snprintf( pcPath + iNameLength, ( size_t ) ( ffconfigMAX_FILENAME - iNameLength ) , "%s%s",\r
1671                                                                 pcPath[ iNameLength - 1 ] == '/' ? "" : "/", pxFindData->pcFileName );\r
1672 \r
1673                                                         /* Let pxFindData point to the next element before\r
1674                                                         the current will get removed. */\r
1675                                                         iNext = ff_findnext( pxFindData );\r
1676 \r
1677                                                         /* Remove the contents of this directory. */\r
1678                                                         iResult = ff_deltree_recurse( pcPath );\r
1679                                                         if( iResult < 0 )\r
1680                                                         {\r
1681                                                                 iCount = -1;\r
1682                                                                 break;\r
1683                                                         }\r
1684                                                         iCount += iResult;\r
1685                                                         /* remove the directory itself */\r
1686                                                         xError = ff_rmdir( pcPath );\r
1687                                                         if( xError != 0 )\r
1688                                                         {\r
1689                                                                 FF_PRINTF( "ff_rmdir( %s ): errno %d\n", pcPath, stdioGET_ERRNO() );\r
1690                                                         }\r
1691                                                         else\r
1692                                                         {\r
1693                                                                 iCount++;\r
1694                                                         }\r
1695                                                 }\r
1696                                                 else\r
1697                                                 {\r
1698                                                         iNext = ff_findnext( pxFindData );\r
1699                                                 }\r
1700                                         }\r
1701                                         else if( ( pass == 1 ) && ( xIsDir == pdFALSE ) )\r
1702                                         {\r
1703                                                 snprintf( pcPath + iNameLength, ( size_t ) ( ffconfigMAX_FILENAME - iNameLength ), "%s%s",\r
1704                                                         pcPath[ iNameLength - 1 ] == '/' ? "" : "/", pxFindData->pcFileName );\r
1705 \r
1706                                                 /* Let pxFindData point to the next element before\r
1707                                                 the current will get removed. */\r
1708                                                 iNext = ff_findnext( pxFindData );\r
1709 \r
1710                                                 /* Remove a plain file. */\r
1711                                                 xError = ff_remove( pcPath );\r
1712                                                 if( xError != 0 )\r
1713                                                 {\r
1714                                                         FF_PRINTF( "ff_remove( %s ): errno %d\n", pcPath, stdioGET_ERRNO() );\r
1715                                                 }\r
1716                                                 else\r
1717                                                 {\r
1718                                                         iCount++;\r
1719                                                 }\r
1720                                         }\r
1721                                         else\r
1722                                         {\r
1723                                                 iNext = ff_findnext( pxFindData );\r
1724                                         }\r
1725                                         pcPath[ iNameLength ] = '\0';\r
1726                                 }\r
1727 \r
1728                                 if( FF_GETERROR( iResult ) == FF_ERR_DIR_INVALID_PATH )\r
1729                                 {\r
1730                                         break;\r
1731                                 }\r
1732                                 if( ( FF_GETERROR( iResult ) != FF_ERR_DIR_END_OF_DIR ) && ( FF_GETERROR( iResult ) != FF_ERR_FILE_INVALID_PATH ) )\r
1733                                 {\r
1734                                         FF_PRINTF( "ff_deltree_recurse[%s]: %s\n", pcPath, ( const char * ) FF_GetErrMessage( iResult ) );\r
1735                                 }\r
1736                         }\r
1737                         ffconfigFREE( pxFindData );\r
1738                 }\r
1739                 else\r
1740                 {\r
1741                         iCount = -1;\r
1742                         stdioSET_ERRNO( pdFREERTOS_ERRNO_ENOMEM );\r
1743                 }\r
1744 \r
1745                 return iCount;\r
1746         }\r
1747 #endif /* ffconfigUSE_DELTREE */\r
1748 /*-----------------------------------------------------------*/\r
1749 \r
1750 int prvFFErrorToErrno( FF_Error_t xError )\r
1751 {\r
1752         if( FF_isERR( xError ) == pdFALSE )\r
1753         {\r
1754                 return 0;\r
1755         }\r
1756 \r
1757         /* Store the last +FAT error code received. */\r
1758         stdioSET_FF_ERROR( xError );\r
1759 \r
1760         switch( FF_GETERROR( xError ) )\r
1761         {\r
1762         /* Global Error Codes. */\r
1763         case FF_ERR_NONE:                                                       return 0;                       /* No Error. */\r
1764 \r
1765         case FF_ERR_NULL_POINTER:                                       return pdFREERTOS_ERRNO_EBADF;          /* pxIOManager was NULL. */\r
1766         case FF_ERR_NOT_ENOUGH_MEMORY:                          return pdFREERTOS_ERRNO_ENOMEM;         /* malloc() failed! - Could not allocate handle memory. */\r
1767         case FF_ERR_DEVICE_DRIVER_FAILED:                       return pdFREERTOS_ERRNO_EIO;            /* The Block Device driver reported a FATAL error, cannot continue. */\r
1768 \r
1769         /* User return codes for Rd/Wr functions:. */\r
1770         case FF_ERR_IOMAN_DRIVER_BUSY:                          return pdFREERTOS_ERRNO_EBUSY;          /* 10. */\r
1771         case FF_ERR_IOMAN_DRIVER_FATAL_ERROR:           return pdFREERTOS_ERRNO_EUNATCH;        /* Protocol driver not attached. */\r
1772 \r
1773         /* IOMAN Error Codes. */\r
1774         case FF_ERR_IOMAN_BAD_BLKSIZE:                          return pdFREERTOS_ERRNO_EINVAL;         /* The provided blocksize was not a multiple of 512. */\r
1775         case FF_ERR_IOMAN_BAD_MEMSIZE:                          return pdFREERTOS_ERRNO_EINVAL;         /* The memory size was not a multiple of the blocksize. */\r
1776         case FF_ERR_IOMAN_DEV_ALREADY_REGD:                     return pdFREERTOS_ERRNO_EADDRINUSE;     /* Device was already registered. Use FF_UnRegister() to re-use this IOMAN with another device. */\r
1777         case FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION:       return pdFREERTOS_ERRNO_ENOMEDIUM;      /* A mountable partition could not be found on the device. */\r
1778         case FF_ERR_IOMAN_INVALID_FORMAT:                       return pdFREERTOS_ERRNO_EFTYPE;         /* The. */\r
1779         case FF_ERR_IOMAN_INVALID_PARTITION_NUM:        return pdFREERTOS_ERRNO_EINVAL;         /* The partition number provided was out of range. */\r
1780         case FF_ERR_IOMAN_NOT_FAT_FORMATTED:            return pdFREERTOS_ERRNO_EFTYPE;         /* The partition did not look like a FAT partition. */\r
1781         case FF_ERR_IOMAN_DEV_INVALID_BLKSIZE:          return pdFREERTOS_ERRNO_EINVAL;         /* IOMAN object BlkSize is not compatible with the blocksize of this device driver. */\r
1782         case FF_ERR_IOMAN_PARTITION_MOUNTED:            return pdFREERTOS_ERRNO_EADDRINUSE;     /* Device is in use by an actively mounted partition. Unmount the partition first. */\r
1783         case FF_ERR_IOMAN_ACTIVE_HANDLES:                       return pdFREERTOS_ERRNO_EBUSY;          /* The partition cannot be unmounted until all active file handles are closed. (There may also be active handles on the cache). */\r
1784         case FF_ERR_IOMAN_GPT_HEADER_CORRUPT:           return pdFREERTOS_ERRNO_EBADE;          /* The GPT partition table appears to be corrupt, refusing to mount. */\r
1785         case FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE:        return pdFREERTOS_ERRNO_ENOSPC;         /* 22. */\r
1786         case FF_ERR_IOMAN_OUT_OF_BOUNDS_READ:           return pdFREERTOS_ERRNO_ESPIPE;         /* 23, return 'Illegal seek'. */\r
1787         case FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE:          return pdFREERTOS_ERRNO_ESPIPE;         /* 24. */\r
1788         case FF_ERR_IOMAN_DRIVER_NOMEDIUM:                      return pdFREERTOS_ERRNO_ENOMEDIUM;      /* The medium (e.g. SD-card) has been extracted. */\r
1789 \r
1790         /* File Error Codes                         30 +. */\r
1791         case FF_ERR_FILE_ALREADY_OPEN:                          return pdFREERTOS_ERRNO_EALREADY;       /* File is in use. */\r
1792         case FF_ERR_FILE_NOT_FOUND:                                     return pdFREERTOS_ERRNO_ENOENT;         /* File was not found. */\r
1793         case FF_ERR_FILE_OBJECT_IS_A_DIR:                       return pdFREERTOS_ERRNO_EISDIR;         /* Tried to FF_Open() a Directory. */\r
1794         case FF_ERR_FILE_IS_READ_ONLY:                          return pdFREERTOS_ERRNO_EROFS;          /* Tried to FF_Open() a file marked read only. */\r
1795         case FF_ERR_FILE_INVALID_PATH:                          return pdFREERTOS_ERRNO_ENOTDIR;        /* The path of the file was not found. */\r
1796         case FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE:      return pdFREERTOS_ERRNO_EACCES;         /* 35. */\r
1797         case FF_ERR_FILE_NOT_OPENED_IN_READ_MODE:       return pdFREERTOS_ERRNO_EACCES;         /* 36. */\r
1798         case FF_ERR_FILE_EXTEND_FAILED:                         return pdFREERTOS_ERRNO_ENOSPC;         /* Could not extend the file. */\r
1799         case FF_ERR_FILE_DESTINATION_EXISTS:            return pdFREERTOS_ERRNO_EEXIST;         /* 38. */\r
1800         case FF_ERR_FILE_SOURCE_NOT_FOUND:                      return pdFREERTOS_ERRNO_ENOENT;         /* 39. */\r
1801         case FF_ERR_FILE_DIR_NOT_FOUND:                         return pdFREERTOS_ERRNO_ENOENT;         /* 40. */\r
1802         case FF_ERR_FILE_COULD_NOT_CREATE_DIRENT:       return pdFREERTOS_ERRNO_EIO;            /* 41. */\r
1803         case FF_ERR_FILE_BAD_HANDLE:                            return pdFREERTOS_ERRNO_EBADF;          /* A file handle was invalid. */\r
1804         case FF_ERR_FILE_MEDIA_REMOVED:                         return pdFREERTOS_ERRNO_ENODEV;         /* File handle got invalid because media was removed. */\r
1805         case FF_ERR_FILE_SEEK_INVALID_POSITION:         return pdFREERTOS_ERRNO_ESPIPE;         /* Illegal position, outside the file's space */\r
1806         case FF_ERR_FILE_SEEK_INVALID_ORIGIN:           return pdFREERTOS_ERRNO_EINVAL;         /* Seeking beyond end of file. */\r
1807 \r
1808         /* Directory Error Codes                    50 +. */\r
1809         case FF_ERR_DIR_OBJECT_EXISTS:                          return pdFREERTOS_ERRNO_EEXIST;         /* A file or folder of the same name already exists in the current directory. */\r
1810         case FF_ERR_DIR_DIRECTORY_FULL:                         return pdFREERTOS_ERRNO_ENOSPC;         /* No more items could be added to the directory. */\r
1811         case FF_ERR_DIR_END_OF_DIR:                                     return pdFREERTOS_ERRNO_ENMFILE;        /*/. */\r
1812         case FF_ERR_DIR_NOT_EMPTY:                                      return pdFREERTOS_ERRNO_ENOTEMPTY;      /* Cannot delete a directory that contains files or folders. */\r
1813         case FF_ERR_DIR_INVALID_PATH:                           return pdFREERTOS_ERRNO_EINVAL;         /* Could not find the directory specified by the path. */\r
1814         case FF_ERR_DIR_CANT_EXTEND_ROOT_DIR:           return pdFREERTOS_ERRNO_ENOSPC;         /* Can't extend the root dir. */\r
1815         case FF_ERR_DIR_EXTEND_FAILED:                          return pdFREERTOS_ERRNO_ENOSPC;         /* Not enough space to extend the directory. */\r
1816         case FF_ERR_DIR_NAME_TOO_LONG:                          return pdFREERTOS_ERRNO_ENAMETOOLONG;/* Name exceeds the number of allowed characters for a filename. */\r
1817 \r
1818         /* Fat Error Codes                          70 +. */\r
1819         case FF_ERR_FAT_NO_FREE_CLUSTERS:                       return pdFREERTOS_ERRNO_ENOSPC;         /* No more free space is available on the disk. */\r
1820 \r
1821         /* UNICODE Error Codes                      100 +. */\r
1822         case FF_ERR_UNICODE_INVALID_CODE:                       return pdFREERTOS_ERRNO_EBADE;          /* An invalid Unicode character was provided!. */\r
1823         case FF_ERR_UNICODE_DEST_TOO_SMALL:                     return pdFREERTOS_ERRNO_ENOBUFS;        /* Not enough space in the UTF-16 buffer to encode the entire sequence as UTF-16. */\r
1824         case FF_ERR_UNICODE_INVALID_SEQUENCE:           return pdFREERTOS_ERRNO_EILSEQ;         /* An invalid UTF-16 sequence was encountered. */\r
1825         case FF_ERR_UNICODE_CONVERSION_EXCEEDED:        return pdFREERTOS_ERRNO_ENAMETOOLONG;/* Filename exceeds MAX long-filename length when converted to UTF-16. */\r
1826         }\r
1827 \r
1828         return pdFREERTOS_ERRNO_EFAULT;\r
1829 }\r
1830 /*-----------------------------------------------------------*/\r
1831 \r
1832 #if( ffconfigHAS_CWD == 1 )\r
1833 \r
1834         void ff_free_CWD_space( void )\r
1835         {\r
1836         WorkingDirectory_t *pxSpace;\r
1837 \r
1838                 /* Obtain the CWD used by the current task. */\r
1839                 pxSpace = ( WorkingDirectory_t * ) pvTaskGetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET );\r
1840                 if( pxSpace != NULL )\r
1841                 {\r
1842                         vTaskSetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET, ( void * ) NULL );\r
1843                         ffconfigFREE( pxSpace );\r
1844                 }\r
1845         }\r
1846 \r
1847 #endif /* ffconfigHAS_CWD */\r
1848 /*-----------------------------------------------------------*/\r
1849 \r
1850 #if( ffconfigHAS_CWD == 1 )\r
1851 \r
1852         static WorkingDirectory_t *pxFindCWD( void )\r
1853         {\r
1854         WorkingDirectory_t *pxReturn;\r
1855 \r
1856                 /* Obtain the CWD used by the current task. */\r
1857                 pxReturn = ( WorkingDirectory_t * ) pvTaskGetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET );\r
1858 \r
1859                 if( pxReturn == NULL )\r
1860                 {\r
1861                         /* This task does not yet have a WorkingDirectory_t structure - create and\r
1862                         initialise one now. */\r
1863                         pxReturn = ( WorkingDirectory_t * ) ffconfigMALLOC( sizeof( WorkingDirectory_t ) );\r
1864 \r
1865                         if( pxReturn != NULL )\r
1866                         {\r
1867                                 pxReturn->pcCWD[ 0 ] = '\0';\r
1868                                 vTaskSetThreadLocalStoragePointer( NULL, stdioCWD_THREAD_LOCAL_OFFSET, ( void * ) pxReturn );\r
1869                         }\r
1870                 }\r
1871 \r
1872                 return pxReturn;\r
1873         }\r
1874 \r
1875 #endif /* ffconfigHAS_CWD */\r
1876 /*-----------------------------------------------------------*/\r
1877 \r
1878 #if( ffconfigHAS_CWD == 1 )\r
1879 \r
1880         static const char *prvProcessRelativePaths( const char *pcPath )\r
1881         {\r
1882         const char *pcReturn;\r
1883         char *pcChar, *pcTokenStart = NULL, *pcFollowingToken, cPreviousChar = 0x00;\r
1884         BaseType_t xByte;\r
1885 \r
1886                 /* Scan the string looking for a relative path. */\r
1887                 pcReturn = pcPath;\r
1888                 pcChar = ( char * ) pcReturn;\r
1889 \r
1890                 configASSERT( pcPath );\r
1891 \r
1892                 while( *pcChar != 0x00 )\r
1893                 {\r
1894                         if( *pcChar == '.' )\r
1895                         {\r
1896                                 /* A potential relative path was found.  Is this a "." or a "..". */\r
1897                                 if( *( pcChar + 1 ) == '.' )\r
1898                                 {\r
1899                                         /* Nothing can be done if this is at the start of the string. */\r
1900                                         if( pcTokenStart != NULL )\r
1901                                         {\r
1902                                                 /* A ".." was found.  Where does the next token start? */\r
1903                                                 pcFollowingToken = pcChar + 2;\r
1904 \r
1905                                                 if( *pcFollowingToken == '/' )\r
1906                                                 {\r
1907                                                         /* The next token starts after the "../" */\r
1908                                                         pcFollowingToken += sizeof( char );\r
1909                                                 }\r
1910 \r
1911                                                 /* Remove the ".." and the previous token. */\r
1912                                                 xByte = 0;\r
1913                                                 while( pcFollowingToken[ xByte ] != 0x00 )\r
1914                                                 {\r
1915                                                         pcTokenStart[ xByte ] = pcFollowingToken[ xByte ];\r
1916                                                         xByte++;\r
1917                                                 }\r
1918 \r
1919                                                 /* Terminate. */\r
1920                                                 pcTokenStart[ xByte ] = 0x00;\r
1921 \r
1922                                                 /* The pointer to the previous token will now be wrong if\r
1923                                                 there are multiple if "../.." appears in the string.  So\r
1924                                                 reset the variables to continue scanning. */\r
1925                                                 pcChar = ( char * ) pcReturn;\r
1926                                                 cPreviousChar = 0x00;\r
1927                                                 pcTokenStart = NULL;\r
1928                                                 continue;\r
1929                                         }\r
1930                                 }\r
1931                                 else\r
1932                                 {\r
1933                                         /* A "." was found.  Remove it. */\r
1934                                 }\r
1935                         }\r
1936 \r
1937                         if( cPreviousChar == '/' )\r
1938                         {\r
1939                                 /* This is the start of a new token. */\r
1940                                 pcTokenStart = pcChar;\r
1941                         }\r
1942 \r
1943                         cPreviousChar = *pcChar;\r
1944                         pcChar++;\r
1945                 }\r
1946 \r
1947                 /* Make sure there is no / on the end of the string, being careful not to\r
1948                 remove the / at the beginning of the string. */\r
1949                 if( *( pcChar - 1 ) == '/' )\r
1950                 {\r
1951                         if( ( pcChar - 1 ) != pcReturn )\r
1952                         {\r
1953                                 *( pcChar - 1 ) = 0x00;\r
1954                         }\r
1955                 }\r
1956 \r
1957                 return pcReturn;\r
1958         }\r
1959 \r
1960 #endif /* ffconfigHAS_CWD */\r
1961 /*-----------------------------------------------------------*/\r
1962 \r
1963 #if( ffconfigHAS_CWD == 1 )\r
1964 \r
1965         /*static*/ const char *prvABSPath( const char *pcPath )\r
1966         {\r
1967         char *pcReturn;\r
1968         WorkingDirectory_t *pxWorkingDirectory = pxFindCWD();\r
1969 \r
1970                 configASSERT( pxWorkingDirectory );\r
1971 \r
1972                 if( ( pcPath[ 0 ] ) == '/' )\r
1973                 {\r
1974                         /* If the path starts with a slash it does not start with a relative\r
1975                         path.  Copy the string into a thread local buffer so it can be\r
1976                         manipulated without risk of attempting to write to read only\r
1977                         memory. */\r
1978                         snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "%s", pcPath );\r
1979                         pcReturn = pxWorkingDirectory->pcFileName;\r
1980                 }\r
1981                 else\r
1982                 {\r
1983                         /* Insert the working directory into the front of the path. */\r
1984                         if( pxWorkingDirectory->pcCWD[ 1 ] == 0x00 )\r
1985                         {\r
1986                                 /* In the root, so don't add a '/' between the CWD and the\r
1987                                 file name. */\r
1988                                 snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "/%s", pcPath );\r
1989                         }\r
1990                         else\r
1991                         {\r
1992                                 snprintf( pxWorkingDirectory->pcFileName, sizeof( pxWorkingDirectory->pcFileName ), "%s/%s", pxWorkingDirectory->pcCWD, pcPath );\r
1993                         }\r
1994 \r
1995                         pcReturn = pxWorkingDirectory->pcFileName;\r
1996                 }\r
1997 \r
1998                 /* Make any adjustments necessitated by relative paths. */\r
1999                 prvProcessRelativePaths( pcReturn );\r
2000 \r
2001                 return pcReturn;\r
2002         }\r
2003 \r
2004 #endif /* ffconfigHAS_CWD */\r
2005 \r
2006 #if( ffconfigTIME_SUPPORT == 1 )\r
2007 \r
2008         static uint32_t prvFileTime( FF_SystemTime_t *pxTime )\r
2009         {\r
2010         FF_TimeStruct_t xTime;\r
2011         time_t xReturn;\r
2012 \r
2013                 xTime.tm_sec = pxTime->Second;\r
2014                 xTime.tm_min = pxTime->Minute;\r
2015                 xTime.tm_hour = pxTime->Hour;\r
2016                 xTime.tm_mday = pxTime->Day;\r
2017                 xTime.tm_mon = pxTime->Month - 1;\r
2018                 xTime.tm_year = pxTime->Year - 1900;\r
2019 \r
2020                 xReturn = FreeRTOS_mktime( &xTime );\r
2021 \r
2022                 return xReturn;\r
2023         }\r
2024 \r
2025 #endif\r
2026 /*-----------------------------------------------------------*/\r
2027 \r
2028 \r