]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-FAT-SL/fat_sl/common/file.c
Slight modification to license blub text in header comments.
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-FAT-SL / fat_sl / common / file.c
1 /*\r
2  * FreeRTOS+FAT FS V1.0.0 (C) 2013 HCC Embedded\r
3  *\r
4  * The FreeRTOS+FAT SL license terms are different to the FreeRTOS license \r
5  * terms.\r
6  * \r
7  * FreeRTOS+FAT SL uses a dual license model that allows the software to be used \r
8  * under a standard GPL open source license, or a commercial license.  The \r
9  * standard GPL license (unlike the modified GPL license under which FreeRTOS \r
10  * itself is distributed) requires that all software statically linked with \r
11  * FreeRTOS+FAT SL is also distributed under the same GPL V2 license terms.  \r
12  * Details of both license options follow:\r
13  * \r
14  * - Open source licensing -\r
15  * FreeRTOS+FAT SL is a free download and may be used, modified, evaluated and\r
16  * distributed without charge provided the user adheres to version two of the \r
17  * GNU General Public License (GPL) and does not remove the copyright notice or \r
18  * this text.  The GPL V2 text is available on the gnu.org web site, and on the\r
19  * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.\r
20  * \r
21  * - Commercial licensing -\r
22  * Businesses and individuals who for commercial or other reasons cannot comply\r
23  * with the terms of the GPL V2 license must obtain a commercial license before \r
24  * incorporating FreeRTOS+FAT SL into proprietary software for distribution in \r
25  * any form.  Commercial licenses can be purchased from \r
26  * http://shop.freertos.org/fat_sl and do not require any source files to be \r
27  * changed.\r
28  *\r
29  * FreeRTOS+FAT SL is distributed in the hope that it will be useful.  You\r
30  * cannot use FreeRTOS+FAT SL unless you agree that you use the software 'as\r
31  * is'.  FreeRTOS+FAT SL is provided WITHOUT ANY WARRANTY; without even the\r
32  * implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A\r
33  * PARTICULAR PURPOSE. Real Time Engineers Ltd. and HCC Embedded disclaims all\r
34  * conditions and terms, be they implied, expressed, or statutory.\r
35  *\r
36  * http://www.FreeRTOS.org\r
37  * http://www.FreeRTOS.org/FreeRTOS-Plus\r
38  *\r
39  */\r
40 \r
41 #include "../../api/fat_sl.h"\r
42 #include "../../psp/include/psp_string.h"\r
43 \r
44 #include "util.h"\r
45 #include "volume.h"\r
46 #include "drv.h"\r
47 #include "fat.h"\r
48 #include "dir.h"\r
49 #include "file.h"\r
50 \r
51 #include "../../version/ver_fat_sl.h"\r
52 #if VER_FAT_SL_MAJOR != 3 || VER_FAT_SL_MINOR != 2\r
53  #error Incompatible FAT_SL version number!\r
54 #endif\r
55 \r
56 static unsigned char _f_emptywritebuffer ( void );\r
57 \r
58 \r
59 /****************************************************************************\r
60  *\r
61  * fn_filelength\r
62  *\r
63  * Get a file length\r
64  *\r
65  * INPUTS\r
66  *\r
67  * filename - file whose length is needed\r
68  *\r
69  * RETURNS\r
70  *\r
71  * length of the file\r
72  *\r
73  ***************************************************************************/\r
74 \r
75 long fn_filelength ( const char * filename )\r
76 {\r
77   F_POS        pos;\r
78   F_DIRENTRY * de;\r
79   F_NAME       fsname;\r
80 \r
81   if ( _f_setfsname( filename, &fsname ) )\r
82   {\r
83     return 0;                                     /*invalid name*/\r
84   }\r
85 \r
86   if ( _f_checknamewc( fsname.filename, fsname.fileext ) )\r
87   {\r
88     return 0;                                                    /*invalid name*/\r
89   }\r
90 \r
91   if ( _f_getvolume() )\r
92   {\r
93     return 0;                     /*can't get the size*/\r
94   }\r
95 \r
96   if ( !_f_findpath( &fsname, &pos ) )\r
97   {\r
98     return 0;\r
99   }\r
100 \r
101   if ( !_f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )\r
102   {\r
103     return 0;\r
104   }\r
105 \r
106   if ( de->attr & F_ATTR_DIR )\r
107   {\r
108     return 0;                                           /*directory*/\r
109   }\r
110 \r
111   return (long)_f_getlong( &de->filesize );\r
112 } /* fn_filelength */\r
113 \r
114 \r
115 \r
116 \r
117 \r
118 /****************************************************************************\r
119  *\r
120  * _f_emptywritebuffer\r
121  *\r
122  * empty write buffer if it contains unwritten data\r
123  *\r
124  * RETURNS\r
125  * error code or zero if successful\r
126  *\r
127  ***************************************************************************/\r
128 \r
129 \r
130 static unsigned char _f_emptywritebuffer ( void )\r
131 {\r
132   unsigned char  ret;\r
133 \r
134   ret = _f_writeglsector( gl_file.pos.sector );\r
135   if ( ret )\r
136   {\r
137     return ret;\r
138   }\r
139 \r
140   gl_file.modified = 0;\r
141 \r
142   gl_file.pos.sector++;\r
143 \r
144   if ( gl_file.pos.sector >= gl_file.pos.sectorend )\r
145   {\r
146     unsigned long  value;\r
147 \r
148     gl_volume.fatsector = (unsigned long)-1;\r
149     ret = _f_getclustervalue( gl_file.pos.cluster, &value );\r
150     if ( ret )\r
151     {\r
152       return ret;\r
153     }\r
154 \r
155     if ( ( value >= 2 ) && ( value < F_CLUSTER_RESERVED ) ) /*we are in chain*/\r
156     {\r
157       gl_file.prevcluster = gl_file.pos.cluster;\r
158       _f_clustertopos( value, &gl_file.pos );    /*go to next cluster*/\r
159     }\r
160     else\r
161     {\r
162       unsigned long  nextcluster;\r
163 \r
164       ret = _f_alloccluster( &nextcluster );\r
165       if ( ret )\r
166       {\r
167         return ret;\r
168       }\r
169 \r
170       ret = _f_setclustervalue( nextcluster, F_CLUSTER_LAST );\r
171       if ( ret )\r
172       {\r
173         return ret;\r
174       }\r
175 \r
176       ret = _f_setclustervalue( gl_file.pos.cluster, nextcluster );\r
177       if ( ret )\r
178       {\r
179         return ret;\r
180       }\r
181 \r
182       gl_file.prevcluster = gl_file.pos.cluster;\r
183 \r
184       _f_clustertopos( nextcluster, &gl_file.pos );\r
185 \r
186       return _f_writefatsector();\r
187     }\r
188   }\r
189 \r
190 \r
191   return F_NO_ERROR;\r
192 } /* _f_emptywritebuffer */\r
193 \r
194 \r
195 \r
196 \r
197 /****************************************************************************\r
198  *\r
199  * _f_extend\r
200  *\r
201  * Extend file to a certain size\r
202  *\r
203  ***************************************************************************/\r
204 static unsigned char _f_extend ( long size )\r
205 {\r
206   unsigned long  _size;\r
207   unsigned char  rc;\r
208 \r
209   size -= gl_file.filesize;\r
210   _size = (unsigned long)size;\r
211 \r
212   rc = _f_getcurrsector();\r
213   if ( rc )\r
214   {\r
215     return rc;\r
216   }\r
217 \r
218   psp_memset( gl_sector + gl_file.relpos, 0, ( F_SECTOR_SIZE - gl_file.relpos ) );\r
219 \r
220   if ( gl_file.relpos + _size > F_SECTOR_SIZE )\r
221   {\r
222     _size -= ( F_SECTOR_SIZE - gl_file.relpos );\r
223     while ( _size )\r
224     {\r
225       if ( _f_emptywritebuffer() )\r
226       {\r
227         return F_ERR_WRITE;\r
228       }\r
229 \r
230       psp_memset( gl_sector, 0, F_SECTOR_SIZE );\r
231       _size -= ( _size > F_SECTOR_SIZE ? F_SECTOR_SIZE : _size );\r
232     }\r
233   }\r
234   else\r
235   {\r
236     _size += gl_file.relpos;\r
237   }\r
238 \r
239   gl_file.modified = 1;\r
240   gl_file.filesize += size;\r
241   gl_file.abspos = gl_file.filesize & ( ~( F_SECTOR_SIZE - 1 ) );\r
242   gl_file.relpos = _size;\r
243 \r
244   return F_NO_ERROR;\r
245 } /* _f_extend */\r
246 \r
247 \r
248 \r
249 /****************************************************************************\r
250  *\r
251  * _f_fseek\r
252  *\r
253  * subfunction for f_seek it moves position into given offset and read\r
254  * the current sector\r
255  *\r
256  * INPUTS\r
257  * offset - position from start\r
258  *\r
259  * RETURNS\r
260  *\r
261  * error code or zero if successful\r
262  *\r
263  ***************************************************************************/\r
264 static unsigned char _f_fseek ( long offset )\r
265 {\r
266   unsigned long  cluster;\r
267   unsigned long  tmp;\r
268   unsigned char  ret = F_NO_ERROR;\r
269   long           remain;\r
270 \r
271   if ( offset < 0 )\r
272   {\r
273     offset = 0;\r
274   }\r
275 \r
276   if ( ( (unsigned long) offset <= gl_file.filesize )\r
277        && ( (unsigned long) offset >= gl_file.abspos )\r
278        && ( (unsigned long) offset < gl_file.abspos + F_SECTOR_SIZE ) )\r
279   {\r
280     gl_file.relpos = (unsigned short)( offset - gl_file.abspos );\r
281   }\r
282   else\r
283   {\r
284     if ( gl_file.modified )\r
285     {\r
286       ret = _f_writeglsector( (unsigned long)-1 );\r
287       if ( ret )\r
288       {\r
289         gl_file.mode = F_FILE_CLOSE; /*cant accessed any more*/\r
290         return ret;\r
291       }\r
292     }\r
293 \r
294     if ( gl_file.startcluster )\r
295     {\r
296       gl_file.abspos = 0;\r
297       gl_file.relpos = 0;\r
298       gl_file.prevcluster = 0;\r
299       gl_file.pos.cluster = gl_file.startcluster;\r
300       remain = gl_file.filesize;\r
301 \r
302       tmp = gl_volume.bootrecord.sector_per_cluster;\r
303       tmp *= F_SECTOR_SIZE;   /* set to cluster size */\r
304 \r
305       /*calc cluster*/\r
306       gl_volume.fatsector = (unsigned long)-1;\r
307       while ( (unsigned long)offset >= tmp )\r
308       {\r
309         ret = _f_getclustervalue( gl_file.pos.cluster, &cluster );\r
310         if ( ret )\r
311         {\r
312           gl_file.mode = F_FILE_CLOSE;\r
313           return ret;\r
314         }\r
315 \r
316         if ( (long) tmp >= remain )\r
317         {\r
318           break;\r
319         }\r
320 \r
321         remain -= tmp;\r
322         offset -= tmp;\r
323         gl_file.abspos += tmp;\r
324         if ( cluster >= F_CLUSTER_RESERVED )\r
325         {\r
326           break;\r
327         }\r
328 \r
329         gl_file.prevcluster = gl_file.pos.cluster;\r
330         gl_file.pos.cluster = cluster;\r
331       }\r
332 \r
333       _f_clustertopos( gl_file.pos.cluster, &gl_file.pos );\r
334       if ( remain && offset )\r
335       {\r
336         while ( ( offset > (long) F_SECTOR_SIZE )\r
337                && ( remain > (long) F_SECTOR_SIZE ) )\r
338         {\r
339           gl_file.pos.sector++;\r
340           offset -= F_SECTOR_SIZE;\r
341           remain -= F_SECTOR_SIZE;\r
342           gl_file.abspos += F_SECTOR_SIZE;\r
343         }\r
344       }\r
345 \r
346       if ( remain < offset )\r
347       {\r
348         gl_file.relpos = (unsigned short)remain;\r
349         ret = _f_extend( gl_file.filesize + offset - remain );\r
350       }\r
351       else\r
352       {\r
353         gl_file.relpos = (unsigned short)offset;\r
354       }\r
355     }\r
356   }\r
357 \r
358   return ret;\r
359 } /* _f_fseek */\r
360 \r
361 \r
362 \r
363 /****************************************************************************\r
364  *\r
365  * fn_open\r
366  *\r
367  * open a file for reading/writing/appending\r
368  *\r
369  * INPUTS\r
370  *\r
371  * filename - which file need to be opened\r
372  * mode - string how to open ("r"-read, "w"-write, "w+"-overwrite, "a"-append\r
373  *\r
374  * RETURNS\r
375  *\r
376  * F_FILE pointer if successfully\r
377  * 0 - if any error\r
378  *\r
379  ***************************************************************************/\r
380 F_FILE * fn_open ( const char * filename, const char * mode )\r
381 {\r
382   F_DIRENTRY    * de;\r
383   F_NAME          fsname;\r
384   unsigned short  date;\r
385   unsigned short  time;\r
386   unsigned char   m_mode = F_FILE_CLOSE;\r
387 \r
388   if ( mode[1] == 0 )\r
389   {\r
390     if ( mode[0] == 'r' )\r
391     {\r
392       m_mode = F_FILE_RD;\r
393     }\r
394 \r
395     if ( mode[0] == 'w' )\r
396     {\r
397       m_mode = F_FILE_WR;\r
398     }\r
399 \r
400     if ( mode[0] == 'a' )\r
401     {\r
402       m_mode = F_FILE_A;\r
403     }\r
404   }\r
405 \r
406   if ( ( mode[1] == '+' ) && ( mode[2] == 0 ) )\r
407   {\r
408     if ( mode[0] == 'r' )\r
409     {\r
410       m_mode = F_FILE_RDP;\r
411     }\r
412 \r
413     if ( mode[0] == 'w' )\r
414     {\r
415       m_mode = F_FILE_WRP;\r
416     }\r
417 \r
418     if ( mode[0] == 'a' )\r
419     {\r
420       m_mode = F_FILE_AP;\r
421     }\r
422 \r
423   }\r
424 \r
425   if ( m_mode == F_FILE_CLOSE )\r
426   {\r
427     return 0;                                       /*invalid mode*/\r
428   }\r
429 \r
430   if ( _f_setfsname( filename, &fsname ) )\r
431   {\r
432     return 0;                                     /*invalid name*/\r
433   }\r
434 \r
435   if ( _f_checknamewc( fsname.filename, fsname.fileext ) )\r
436   {\r
437     return 0;                                                    /*invalid name*/\r
438   }\r
439 \r
440   if ( fsname.filename[0] == '.' )\r
441   {\r
442     return 0;\r
443   }\r
444 \r
445   if ( _f_getvolume() )\r
446   {\r
447     return 0;                     /*cant open any*/\r
448   }\r
449 \r
450   if ( gl_file.mode != F_FILE_CLOSE )\r
451   {\r
452     return 0;\r
453   }\r
454 \r
455   psp_memset( &gl_file, 0, 21 );\r
456 \r
457   if ( !_f_findpath( &fsname, &gl_file.dirpos ) )\r
458   {\r
459     return 0;\r
460   }\r
461 \r
462   switch ( m_mode )\r
463   {\r
464     case F_FILE_RDP:   /*r*/\r
465     case F_FILE_RD:   /*r*/\r
466       if ( !_f_findfilewc( fsname.filename, fsname.fileext, &gl_file.dirpos, &de, 0 ) )\r
467       {\r
468         return 0;\r
469       }\r
470 \r
471       if ( de->attr & F_ATTR_DIR )\r
472       {\r
473         return 0;                                      /*directory*/\r
474       }\r
475 \r
476       gl_file.startcluster = _f_getdecluster( de );\r
477 \r
478       if ( gl_file.startcluster )\r
479       {\r
480         _f_clustertopos( gl_file.startcluster, &gl_file.pos );\r
481         gl_file.filesize = _f_getlong( &de->filesize );\r
482         gl_file.abspos = (unsigned long) (-1 * (long) F_SECTOR_SIZE);\r
483         if ( _f_fseek( 0 ) )\r
484         {\r
485           return 0;\r
486         }\r
487       }\r
488 \r
489 #if F_FILE_CHANGED_EVENT\r
490       if ( m_mode == F_FILE_RDP )\r
491       {\r
492         _f_createfullname( gl_file.filename, sizeof( gl_file.filename ), fsname.path, fsname.filename, fsname.fileext );\r
493       }\r
494 \r
495 #endif\r
496 \r
497       break;\r
498 \r
499     case F_FILE_AP:\r
500     case F_FILE_A: /*a*/\r
501       psp_memcpy( &( gl_file.pos ), &( gl_file.dirpos ), sizeof( F_POS ) );\r
502       if ( _f_findfilewc( fsname.filename, fsname.fileext, &gl_file.dirpos, &de, 0 ) )\r
503       {\r
504         if ( de->attr & ( F_ATTR_DIR | F_ATTR_READONLY ) )\r
505         {\r
506           return 0;\r
507         }\r
508 \r
509         gl_file.startcluster = _f_getdecluster( de );\r
510         gl_file.filesize = _f_getlong( &de->filesize );\r
511 \r
512         if ( gl_file.startcluster )\r
513         {\r
514           _f_clustertopos( gl_file.startcluster, &gl_file.pos );\r
515           gl_file.abspos = (unsigned long) (-1 * (long) F_SECTOR_SIZE);   /*forcing seek to read 1st sector! abspos=0;*/\r
516           if ( _f_fseek( (long)gl_file.filesize ) )\r
517           {\r
518             gl_file.mode = F_FILE_CLOSE;\r
519             return 0;\r
520           }\r
521         }\r
522       }\r
523       else\r
524       {\r
525         psp_memcpy( &( gl_file.dirpos ), &( gl_file.pos ), sizeof( F_POS ) );\r
526         _f_clustertopos( gl_file.dirpos.cluster, &gl_file.pos );\r
527 \r
528         if ( _f_addentry( &fsname, &gl_file.dirpos, &de ) )\r
529         {\r
530           return 0;                                                  /*couldnt be added*/\r
531         }\r
532 \r
533         de->attr |= F_ATTR_ARC;         /*set as archiv*/\r
534         if ( _f_writeglsector( (unsigned long)-1 ) )\r
535         {\r
536           return 0;\r
537         }\r
538       }\r
539 \r
540  #if F_FILE_CHANGED_EVENT\r
541       _f_createfullname( gl_file.filename, sizeof( gl_file.filename ), fsname.path, fsname.filename, fsname.fileext );\r
542  #endif\r
543       break;\r
544 \r
545 \r
546     case F_FILE_WR:  /*w*/\r
547     case F_FILE_WRP: /*w+*/\r
548       _f_clustertopos( gl_file.dirpos.cluster, &gl_file.pos );\r
549       if ( _f_findfilewc( fsname.filename, fsname.fileext, &gl_file.pos, &de, 0 ) )\r
550       {\r
551         unsigned long  cluster = _f_getdecluster( de );    /*exist*/\r
552 \r
553         if ( de->attr & ( F_ATTR_DIR | F_ATTR_READONLY ) )\r
554         {\r
555           return 0;\r
556         }\r
557 \r
558         psp_memcpy( &( gl_file.dirpos ), &( gl_file.pos ), sizeof( F_POS ) );\r
559 \r
560         _f_setlong( de->filesize, 0 );  /*reset size;*/\r
561         de->attr |= F_ATTR_ARC;         /*set as archiv*/\r
562         _f_setword( de->clusterlo, 0 ); /*no points anywhere*/\r
563         _f_setword( de->clusterhi, 0 );\r
564 \r
565         if ( gl_volume.mediatype == F_FAT32_MEDIA )\r
566         {\r
567           f_igettimedate( &time, &date );\r
568           _f_setword( &de->crtdate, date );        /*if there is realtime clock then creation date could be set from*/\r
569           _f_setword( &de->crttime, time );        /*if there is realtime clock then creation time could be set from*/\r
570           _f_setword( &de->lastaccessdate, date ); /*if there is realtime clock then creation date could be set from*/\r
571         }\r
572 \r
573         if ( _f_writeglsector( (unsigned long)-1 ) )\r
574         {\r
575           return 0;\r
576         }\r
577 \r
578         if ( _f_removechain( cluster ) )\r
579         {\r
580           return 0;                                /*remove */\r
581         }\r
582       }\r
583       else\r
584       {\r
585         if ( _f_addentry( &fsname, &gl_file.dirpos, &de ) )\r
586         {\r
587           return 0;                                                  /*couldnt be added*/\r
588         }\r
589 \r
590         psp_memset( &gl_file, 0, 21 );\r
591         de->attr |= F_ATTR_ARC;         /*set as archiv*/\r
592         if ( _f_writeglsector( (unsigned long)-1 ) )\r
593         {\r
594           return 0;\r
595         }\r
596       }\r
597 \r
598  #if F_FILE_CHANGED_EVENT\r
599       _f_createfullname( gl_file.filename, sizeof( gl_file.filename ), fsname.path, fsname.filename, fsname.fileext );\r
600  #endif\r
601 \r
602       break;\r
603 \r
604     default:\r
605       return 0;        /*invalid mode*/\r
606   } /* switch */\r
607 \r
608   if ( ( m_mode != F_FILE_RD ) && ( gl_file.startcluster == 0 ) )\r
609   {\r
610     gl_volume.fatsector = (unsigned long)-1;\r
611     if ( _f_alloccluster( &( gl_file.startcluster ) ) )\r
612     {\r
613       return 0;\r
614     }\r
615 \r
616     _f_clustertopos( gl_file.startcluster, &gl_file.pos );\r
617     if ( _f_setclustervalue( gl_file.startcluster, F_CLUSTER_LAST ) )\r
618     {\r
619       return 0;\r
620     }\r
621 \r
622     if ( _f_writefatsector() )\r
623     {\r
624       return 0;\r
625     }\r
626   }\r
627 \r
628 \r
629   gl_file.mode = m_mode; /* lock it */\r
630   return (F_FILE *)1;\r
631 } /* fn_open */\r
632 \r
633 \r
634 /****************************************************************************\r
635  * _f_updatefileentry\r
636  * Updated a file directory entry or removes the entry\r
637  * and the fat chain belonging to it.\r
638  ***************************************************************************/\r
639 static unsigned char _f_updatefileentry ( int remove )\r
640 {\r
641   F_DIRENTRY    * de;\r
642   unsigned short  date;\r
643   unsigned short  time;\r
644 \r
645   de = (F_DIRENTRY *)( gl_sector + sizeof( F_DIRENTRY ) * gl_file.dirpos.pos );\r
646   if ( _f_readglsector( gl_file.dirpos.sector ) || remove )\r
647   {\r
648     _f_setdecluster( de, 0 );\r
649     _f_setlong( &de->filesize, 0 );\r
650     (void)_f_writeglsector( (unsigned long)-1 );\r
651     (void)_f_removechain( gl_file.startcluster );\r
652     return F_ERR_WRITE;\r
653   }\r
654 \r
655   _f_setdecluster( de, gl_file.startcluster );\r
656   _f_setlong( &de->filesize, gl_file.filesize );\r
657   f_igettimedate( &time, &date );\r
658   _f_setword( &de->cdate, date );  /*if there is realtime clock then creation date could be set from*/\r
659   _f_setword( &de->ctime, time );  /*if there is realtime clock then creation time could be set from*/\r
660   if ( gl_volume.mediatype == F_FAT32_MEDIA )\r
661   {\r
662     _f_setword( &de->lastaccessdate, date );  /*if there is realtime clock then creation date could be set from*/\r
663   }\r
664 \r
665   return _f_writeglsector( (unsigned long)-1 );\r
666 } /* _f_updatefileentry */\r
667 \r
668 \r
669 /****************************************************************************\r
670  *\r
671  * fn_close\r
672  *\r
673  * close a previously opened file\r
674  *\r
675  * INPUTS\r
676  *\r
677  * filehandle - which file needs to be closed\r
678  *\r
679  * RETURNS\r
680  *\r
681  * error code or zero if successful\r
682  *\r
683  ***************************************************************************/\r
684 unsigned char fn_close ( F_FILE * f )\r
685 {\r
686   unsigned char  ret;\r
687 \r
688 #if F_FILE_CHANGED_EVENT\r
689   unsigned char  mode;\r
690 #endif\r
691 \r
692   if ( !f )\r
693   {\r
694     return F_ERR_NOTOPEN;\r
695   }\r
696 \r
697   ret = _f_getvolume();\r
698   if ( ret )\r
699   {\r
700     return ret;\r
701   }\r
702 \r
703   if ( gl_file.mode == F_FILE_CLOSE )\r
704   {\r
705     return F_ERR_NOTOPEN;\r
706   }\r
707 \r
708   else if ( gl_file.mode == F_FILE_RD )\r
709   {\r
710     gl_file.mode = F_FILE_CLOSE;\r
711     return F_NO_ERROR;\r
712   }\r
713   else\r
714   {\r
715  #if F_FILE_CHANGED_EVENT\r
716     mode = f->mode;\r
717  #endif\r
718     gl_file.mode = F_FILE_CLOSE;\r
719 \r
720     if ( gl_file.modified )\r
721     {\r
722       if ( _f_writeglsector( (unsigned long)-1 ) )\r
723       {\r
724         (void)_f_updatefileentry( 1 );\r
725         return F_ERR_WRITE;\r
726       }\r
727     }\r
728 \r
729     ret = _f_updatefileentry( 0 );\r
730 \r
731  #if F_FILE_CHANGED_EVENT\r
732     if ( f_filechangedevent && !ret )\r
733     {\r
734       ST_FILE_CHANGED  fc;\r
735       if ( ( mode == F_FILE_WR )  || ( mode == F_FILE_WRP ) )\r
736       {\r
737         fc.action = FACTION_ADDED;\r
738         fc.flags  = FFLAGS_FILE_NAME;\r
739       }\r
740       else if ( ( mode == F_FILE_A )  || ( mode == F_FILE_RDP ) )\r
741       {\r
742         fc.action = FACTION_MODIFIED;\r
743         fc.flags  = FFLAGS_FILE_NAME | FFLAGS_SIZE;\r
744       }\r
745 \r
746       strcpy( fc.filename, f->filename );\r
747       if ( f->filename[0] )\r
748       {\r
749         f_filechangedevent( &fc );\r
750       }\r
751 \r
752       f->filename[0] = '\0';\r
753     }\r
754 \r
755  #endif /* if F_FILE_CHANGED_EVENT */\r
756     return ret;\r
757   }\r
758 } /* fn_close */\r
759 \r
760 \r
761 /****************************************************************************\r
762  *\r
763  * fn_flush\r
764  *\r
765  * flush a previously opened file\r
766  *\r
767  * INPUTS\r
768  *\r
769  * filehandle - which file needs to be closed\r
770  *\r
771  * RETURNS\r
772  *\r
773  * error code or zero if successful\r
774  *\r
775  ***************************************************************************/\r
776 unsigned char fn_flush ( F_FILE * f )\r
777 {\r
778   unsigned char  ret;\r
779 \r
780   if ( !f )\r
781   {\r
782     return F_ERR_NOTOPEN;\r
783   }\r
784 \r
785   ret = _f_getvolume();\r
786   if ( ret )\r
787   {\r
788     return ret;\r
789   }\r
790 \r
791   if ( gl_file.mode == F_FILE_CLOSE )\r
792   {\r
793     return F_ERR_NOTOPEN;\r
794   }\r
795   else if ( gl_file.mode != F_FILE_RD )\r
796   {\r
797     if ( gl_file.modified )\r
798     {\r
799       if ( _f_writeglsector( (unsigned long)-1 ) )\r
800       {\r
801         (void)_f_updatefileentry( 1 );\r
802         return F_ERR_WRITE;\r
803       }\r
804     }\r
805 \r
806     return _f_updatefileentry( 0 );\r
807   }\r
808 \r
809   return F_NO_ERROR;\r
810 } /* fn_flush */\r
811 \r
812 \r
813 /****************************************************************************\r
814  *\r
815  * fn_read\r
816  *\r
817  * read data from file\r
818  *\r
819  * INPUTS\r
820  *\r
821  * buf - where the store data\r
822  * size - size of items to be read\r
823  * _size_t - number of items need to be read\r
824  * filehandle - file where to read from\r
825  *\r
826  * RETURNS\r
827  *\r
828  * with the number of read bytes\r
829  *\r
830  ***************************************************************************/\r
831 long fn_read ( void * buf, long size, long _size_st, F_FILE * f )\r
832 {\r
833   char * buffer = (char *)buf;\r
834   long   retsize;\r
835 \r
836   if ( !f )\r
837   {\r
838     return 0;\r
839   }\r
840 \r
841   if ( ( gl_file.mode & ( F_FILE_RD | F_FILE_RDP | F_FILE_WRP | F_FILE_AP ) ) == 0 )\r
842   {\r
843     return 0;\r
844   }\r
845 \r
846   retsize = size;\r
847   size *= _size_st;\r
848   _size_st = retsize;\r
849   retsize = 0;\r
850 \r
851   if ( _f_getvolume() )\r
852   {\r
853     return 0;                     /*cant read any*/\r
854   }\r
855 \r
856   if ( size + gl_file.relpos + gl_file.abspos >= gl_file.filesize ) /*read len longer than the file*/\r
857   {\r
858     size = (long)( ( gl_file.filesize ) - ( gl_file.relpos ) - ( gl_file.abspos ) ); /*calculate new size*/\r
859   }\r
860 \r
861   if ( size <= 0 )\r
862   {\r
863     return 0;\r
864   }\r
865 \r
866   if ( _f_getcurrsector() )\r
867   {\r
868     gl_file.mode = F_FILE_CLOSE; /*no more read allowed*/\r
869     return 0;\r
870   }\r
871 \r
872   for( ; ; )\r
873   {\r
874     unsigned long  rdsize = (unsigned long)size;\r
875 \r
876     if ( gl_file.relpos == F_SECTOR_SIZE )\r
877     {\r
878       unsigned char  ret;\r
879 \r
880       gl_file.abspos += gl_file.relpos;\r
881       gl_file.relpos = 0;\r
882 \r
883       if ( gl_file.modified )\r
884       {\r
885         ret = _f_writeglsector( (unsigned long)-1 );     /*empty write buffer */\r
886         if ( ret )\r
887         {\r
888           gl_file.mode = F_FILE_CLOSE;         /*no more read allowed*/\r
889           return retsize;\r
890         }\r
891       }\r
892 \r
893       gl_file.pos.sector++;         /*goto next*/\r
894 \r
895       ret = _f_getcurrsector();\r
896       if ( ( ret == F_ERR_EOF ) && ( !size ) )\r
897       {\r
898         return retsize;\r
899       }\r
900 \r
901       if ( ret )\r
902       {\r
903         gl_file.mode = F_FILE_CLOSE;       /*no more read allowed*/\r
904         return retsize;\r
905       }\r
906     }\r
907 \r
908     if ( !size )\r
909     {\r
910       break;\r
911     }\r
912 \r
913     if ( rdsize >= F_SECTOR_SIZE - gl_file.relpos )\r
914     {\r
915       rdsize = (unsigned long)( F_SECTOR_SIZE - gl_file.relpos );\r
916     }\r
917 \r
918     psp_memcpy( buffer, gl_sector + gl_file.relpos, rdsize ); /*always less than 512*/\r
919 \r
920     buffer += rdsize;\r
921     gl_file.relpos += rdsize;\r
922     size -= rdsize;\r
923     retsize += rdsize;\r
924   }\r
925 \r
926   return retsize / _size_st;\r
927 } /* fn_read */\r
928 \r
929 \r
930 /****************************************************************************\r
931  *\r
932  * fn_write\r
933  *\r
934  * write data into file\r
935  *\r
936  * INPUTS\r
937  *\r
938  * buf - where the store data\r
939  * size - size of items to be read\r
940  * size_t - number of items need to be read\r
941  * filehandle - file where to read from\r
942  *\r
943  * RETURNS\r
944  *\r
945  * with the number of read bytes\r
946  *\r
947  ***************************************************************************/\r
948 \r
949 long fn_write ( const void * buf, long size, long _size_st, F_FILE * f )\r
950 {\r
951   char * buffer = (char *)buf;\r
952   long   retsize;\r
953   long   ret = 0;\r
954 \r
955   if ( !f )\r
956   {\r
957     return 0;\r
958   }\r
959 \r
960   if ( ( gl_file.mode & ( F_FILE_WR | F_FILE_A | F_FILE_RDP | F_FILE_WRP | F_FILE_AP ) ) == 0 )\r
961   {\r
962     return 0;\r
963   }\r
964 \r
965   retsize = size;\r
966   size *= _size_st;\r
967   _size_st = retsize;\r
968   retsize = 0;\r
969 \r
970   if ( _f_getvolume() )\r
971   {\r
972     return 0;                     /*can't write*/\r
973   }\r
974 \r
975   if ( ( gl_file.mode ) & ( F_FILE_A | F_FILE_AP ) )\r
976   {\r
977     if ( _f_fseek( (long)gl_file.filesize ) )\r
978     {\r
979       gl_file.mode = F_FILE_CLOSE;\r
980       return 0;\r
981     }\r
982   }\r
983 \r
984   if ( _f_getcurrsector() )\r
985   {\r
986     gl_file.mode = F_FILE_CLOSE;\r
987     return 0;\r
988   }\r
989 \r
990   for( ; ; )\r
991   {\r
992     unsigned long  wrsize = (unsigned long)size;\r
993 \r
994     if ( gl_file.relpos == F_SECTOR_SIZE )\r
995     {     /*now full*/\r
996       if ( gl_file.modified )\r
997       {\r
998         if ( _f_emptywritebuffer() )\r
999         {\r
1000           gl_file.mode = F_FILE_CLOSE;\r
1001           if ( _f_updatefileentry( 0 ) == 0 )\r
1002           {\r
1003             return retsize;\r
1004           }\r
1005           else\r
1006           {\r
1007             return 0;\r
1008           }\r
1009         }\r
1010       }\r
1011       else\r
1012       {\r
1013         gl_file.pos.sector++;       /*goto next*/\r
1014       }\r
1015 \r
1016       gl_file.abspos += gl_file.relpos;\r
1017       gl_file.relpos = 0;\r
1018 \r
1019       if ( wrsize && ( wrsize < F_SECTOR_SIZE ) )\r
1020       {\r
1021         ret = _f_getcurrsector();\r
1022 \r
1023         if ( ret )\r
1024         {\r
1025           if ( ret != F_ERR_EOF )\r
1026           {\r
1027             gl_file.mode = F_FILE_CLOSE;       /*no more read allowed*/\r
1028             return retsize;\r
1029           }\r
1030         }\r
1031       }\r
1032     }\r
1033 \r
1034     if ( !size )\r
1035     {\r
1036       break;\r
1037     }\r
1038 \r
1039     if ( wrsize >= F_SECTOR_SIZE - gl_file.relpos )\r
1040     {\r
1041       wrsize = (unsigned long)( F_SECTOR_SIZE - gl_file.relpos );\r
1042     }\r
1043 \r
1044 \r
1045     psp_memcpy( gl_sector + gl_file.relpos, buffer, wrsize );\r
1046     gl_file.modified = 1;    /*sector is modified*/\r
1047 \r
1048     buffer += wrsize;\r
1049     gl_file.relpos += wrsize;\r
1050     size -= wrsize;\r
1051     retsize += wrsize;\r
1052 \r
1053     if ( gl_file.filesize < gl_file.abspos + gl_file.relpos )\r
1054     {\r
1055       gl_file.filesize = gl_file.abspos + gl_file.relpos;\r
1056     }\r
1057   }\r
1058 \r
1059   return retsize / _size_st;\r
1060 } /* fn_write */\r
1061 \r
1062 \r
1063 \r
1064 /****************************************************************************\r
1065  *\r
1066  * fn_seek\r
1067  *\r
1068  * moves position into given offset in given file\r
1069  *\r
1070  * INPUTS\r
1071  *\r
1072  * filehandle - F_FILE structure which file position needed to be modified\r
1073  * offset - relative position\r
1074  * whence - where to calculate position (F_SEEK_SET,F_SEEK_CUR,F_SEEK_END)\r
1075  *\r
1076  * RETURNS\r
1077  *\r
1078  * 0 - if successfully\r
1079  * other - if any error\r
1080  *\r
1081  ***************************************************************************/\r
1082 \r
1083 \r
1084 unsigned char fn_seek ( F_FILE * f, long offset, unsigned char whence )\r
1085 {\r
1086   unsigned char  ret;\r
1087 \r
1088   if ( !f )\r
1089   {\r
1090     return F_ERR_NOTOPEN;\r
1091   }\r
1092 \r
1093   if ( ( gl_file.mode & ( F_FILE_RD | F_FILE_WR | F_FILE_A | F_FILE_RDP | F_FILE_WRP | F_FILE_AP ) ) == 0 )\r
1094   {\r
1095     return F_ERR_NOTOPEN;\r
1096   }\r
1097 \r
1098   ret = _f_getvolume();\r
1099   if ( ret )\r
1100   {\r
1101     return ret;\r
1102   }\r
1103 \r
1104   if ( whence == F_SEEK_CUR )\r
1105   {\r
1106     return _f_fseek( (long)( gl_file.abspos + gl_file.relpos + offset ) );\r
1107   }\r
1108   else if ( whence == F_SEEK_END )\r
1109   {\r
1110     return _f_fseek( (long)( gl_file.filesize + offset ) );\r
1111   }\r
1112   else if ( whence == F_SEEK_SET )\r
1113   {\r
1114     return _f_fseek( offset );\r
1115   }\r
1116 \r
1117   return F_ERR_NOTUSEABLE;\r
1118 } /* fn_seek */\r
1119 \r
1120 \r
1121 \r
1122 /****************************************************************************\r
1123  *\r
1124  * fn_tell\r
1125  *\r
1126  * Tells the current position of opened file\r
1127  *\r
1128  * INPUTS\r
1129  *\r
1130  * filehandle - which file needs the position\r
1131  *\r
1132  * RETURNS\r
1133  *\r
1134  * position in the file from start\r
1135  *\r
1136  ***************************************************************************/\r
1137 \r
1138 long fn_tell ( F_FILE * f )\r
1139 {\r
1140   if ( !f )\r
1141   {\r
1142     return 0;\r
1143   }\r
1144 \r
1145   if ( ( gl_file.mode & ( F_FILE_RD | F_FILE_WR | F_FILE_A | F_FILE_RDP | F_FILE_WRP | F_FILE_AP ) ) == 0 )\r
1146   {\r
1147     return 0;\r
1148   }\r
1149 \r
1150   return (long)( gl_file.abspos + gl_file.relpos );\r
1151 }\r
1152 \r
1153 \r
1154 \r
1155 /****************************************************************************\r
1156  *\r
1157  * fn_eof\r
1158  *\r
1159  * Tells if the current position is end of file or not\r
1160  *\r
1161  * INPUTS\r
1162  *\r
1163  * filehandle - which file needs the checking\r
1164  *\r
1165  * RETURNS\r
1166  *\r
1167  * 0 - if not EOF\r
1168  * other - if EOF or invalid file handle\r
1169  *\r
1170  ***************************************************************************/\r
1171 \r
1172 unsigned char fn_eof ( F_FILE * f )\r
1173 {\r
1174   if ( !f )\r
1175   {\r
1176     return F_ERR_NOTOPEN;          /*if error*/\r
1177   }\r
1178 \r
1179   if ( gl_file.abspos + gl_file.relpos < gl_file.filesize )\r
1180   {\r
1181     return 0;\r
1182   }\r
1183 \r
1184   return F_ERR_EOF;  /*EOF*/\r
1185 }\r
1186 \r
1187 \r
1188 \r
1189 \r
1190 /****************************************************************************\r
1191  *\r
1192  * fn_rewind\r
1193  *\r
1194  * set the fileposition in the opened file to the begining\r
1195  *\r
1196  * INPUTS\r
1197  *\r
1198  * filehandle - which file needs to be rewinded\r
1199  *\r
1200  * RETURNS\r
1201  *\r
1202  * error code or zero if successful\r
1203  *\r
1204  ***************************************************************************/\r
1205 \r
1206 unsigned char fn_rewind ( F_FILE * filehandle )\r
1207 {\r
1208   return fn_seek( filehandle, 0L, F_SEEK_SET );\r
1209 }\r
1210 \r
1211 \r
1212 \r
1213 /****************************************************************************\r
1214  *\r
1215  * fn_putc\r
1216  *\r
1217  * write a character into file\r
1218  *\r
1219  * INPUTS\r
1220  *\r
1221  * ch - what to write into file\r
1222  * filehandle - file where to write\r
1223  *\r
1224  * RETURNS\r
1225  *\r
1226  * with the number of written bytes (1-success, 0-not successfully)\r
1227  *\r
1228  ***************************************************************************/\r
1229 \r
1230 int fn_putc ( int ch, F_FILE * filehandle )\r
1231 {\r
1232   unsigned char  tmpch = (unsigned char)ch;\r
1233 \r
1234   if ( fn_write( &tmpch, 1, 1, filehandle ) )\r
1235   {\r
1236     return ch;\r
1237   }\r
1238   else\r
1239   {\r
1240     return -1;\r
1241   }\r
1242 }\r
1243 \r
1244 \r
1245 \r
1246 /****************************************************************************\r
1247  *\r
1248  * fn_getc\r
1249  *\r
1250  * get a character from file\r
1251  *\r
1252  * INPUTS\r
1253  *\r
1254  * filehandle - file where to read from\r
1255  *\r
1256  * RETURNS\r
1257  *\r
1258  * with the read character or -1 if read was not successfully\r
1259  *\r
1260  ***************************************************************************/\r
1261 int fn_getc ( F_FILE * filehandle )\r
1262 {\r
1263   unsigned char  ch;\r
1264 \r
1265   if ( !fn_read( &ch, 1, 1, filehandle ) )\r
1266   {\r
1267     return -1;\r
1268   }\r
1269 \r
1270   return (int)ch;\r
1271 }\r
1272 \r
1273 \r
1274 \r
1275 /****************************************************************************\r
1276  *\r
1277  * fn_delete\r
1278  *\r
1279  * delete a file\r
1280  *\r
1281  * INPUTS\r
1282  *\r
1283  * filename - file which wanted to be deleted (with or without path)\r
1284  *\r
1285  * RETURNS\r
1286  *\r
1287  * error code or zero if successful\r
1288  *\r
1289  ***************************************************************************/\r
1290 unsigned char fn_delete ( const char * filename )\r
1291 {\r
1292   F_POS          pos;\r
1293   F_DIRENTRY   * de;\r
1294   F_NAME         fsname;\r
1295   unsigned char  ret;\r
1296 \r
1297   if ( _f_setfsname( filename, &fsname ) )\r
1298   {\r
1299     return F_ERR_INVALIDNAME;                                     /*invalid name*/\r
1300   }\r
1301 \r
1302   if ( _f_checknamewc( fsname.filename, fsname.fileext ) )\r
1303   {\r
1304     return F_ERR_INVALIDNAME;                                                    /*invalid name*/\r
1305   }\r
1306 \r
1307   if ( fsname.filename[0] == '.' )\r
1308   {\r
1309     return F_ERR_NOTFOUND;\r
1310   }\r
1311 \r
1312   ret = _f_getvolume();\r
1313   if ( ret )\r
1314   {\r
1315     return ret;\r
1316   }\r
1317 \r
1318   if ( !( _f_findpath( &fsname, &pos ) ) )\r
1319   {\r
1320     return F_ERR_INVALIDDIR;\r
1321   }\r
1322 \r
1323   if ( !_f_findfilewc( fsname.filename, fsname.fileext, &pos, &de, 0 ) )\r
1324   {\r
1325     return F_ERR_NOTFOUND;\r
1326   }\r
1327 \r
1328   if ( de->attr & F_ATTR_DIR )\r
1329   {\r
1330     return F_ERR_INVALIDDIR;                                /*directory*/\r
1331   }\r
1332 \r
1333   if ( de->attr & F_ATTR_READONLY )\r
1334   {\r
1335     return F_ERR_ACCESSDENIED;                                      /*readonly*/\r
1336   }\r
1337 \r
1338   if ( ( gl_file.mode != F_FILE_CLOSE ) && ( gl_file.dirpos.sector == pos.sector ) && ( gl_file.dirpos.pos == pos.pos ) )\r
1339   {\r
1340     return F_ERR_LOCKED;\r
1341   }\r
1342 \r
1343   de->name[0] = (unsigned char)0xe5; /*removes it*/\r
1344   ret = _f_writeglsector( (unsigned long)-1 );\r
1345   if ( ret )\r
1346   {\r
1347     return ret;\r
1348   }\r
1349 \r
1350   ret = _f_removechain( _f_getdecluster( de ) );\r
1351 \r
1352  #if F_FILE_CHANGED_EVENT\r
1353   if ( f_filechangedevent && !ret )\r
1354   {\r
1355     ST_FILE_CHANGED  fc;\r
1356     fc.action = FACTION_REMOVED;\r
1357     fc.flags = FFLAGS_FILE_NAME;\r
1358 \r
1359     if ( !_f_createfullname( fc.filename, sizeof( fc.filename ), fsname.path, fsname.filename, fsname.fileext ) )\r
1360     {\r
1361       f_filechangedevent( &fc );\r
1362     }\r
1363   }\r
1364 \r
1365  #endif\r
1366 \r
1367   return ret;\r
1368 } /* fn_delete */\r
1369 \r
1370 \r
1371 \r
1372 \r
1373 /****************************************************************************\r
1374  *\r
1375  * _f_seteof\r
1376  *\r
1377  * Set end of file\r
1378  *\r
1379  * INPUT:       f - file pointer\r
1380  *              filesize - required new size\r
1381  * RETURN:      F_NO_ERROR - on success\r
1382  *              other if error\r
1383  *\r
1384  ***************************************************************************/\r
1385 unsigned char _f_seteof ( F_FILE * f, long filesize )\r
1386 {\r
1387   unsigned char  rc = F_NO_ERROR;\r
1388 \r
1389   if ( !f )\r
1390   {\r
1391     return F_ERR_NOTOPEN;        /*if error*/\r
1392   }\r
1393 \r
1394   if ( ( unsigned long) filesize < gl_file.filesize )\r
1395   {\r
1396     rc = _f_fseek( filesize );\r
1397     if ( rc == F_NO_ERROR )\r
1398     {\r
1399       unsigned long  cluster;\r
1400       rc = _f_getclustervalue( gl_file.pos.cluster, &cluster );\r
1401       if ( rc == F_NO_ERROR )\r
1402       {\r
1403         if ( cluster != F_CLUSTER_LAST )\r
1404         {\r
1405           rc = _f_removechain( cluster );\r
1406           if ( rc )\r
1407           {\r
1408             return rc;\r
1409           }\r
1410 \r
1411           rc = _f_setclustervalue( gl_file.pos.cluster, F_CLUSTER_LAST );\r
1412           if ( rc )\r
1413           {\r
1414             return rc;\r
1415           }\r
1416 \r
1417           rc = _f_writefatsector();\r
1418           if ( rc )\r
1419           {\r
1420             return rc;\r
1421           }\r
1422         }\r
1423 \r
1424         gl_file.filesize = (unsigned long)filesize;\r
1425       }\r
1426     }\r
1427   }\r
1428   else if ( (unsigned long) filesize > gl_file.filesize )\r
1429   {\r
1430     rc = _f_fseek( filesize );\r
1431   }\r
1432 \r
1433   return rc;\r
1434 } /* _f_seteof */\r
1435 \r
1436 \r
1437 /****************************************************************************\r
1438  *\r
1439  * fn_seteof\r
1440  *\r
1441  * Set end of file\r
1442  *\r
1443  * INPUT:       f - file pointer\r
1444  *              filesize - required new size\r
1445  * RETURN:      F_NO_ERROR - on success\r
1446  *              other if error\r
1447  *\r
1448  ***************************************************************************/\r
1449 unsigned char fn_seteof ( F_FILE * f )\r
1450 {\r
1451   unsigned char  rc = F_NO_ERROR;\r
1452 \r
1453   rc = _f_seteof( f, ( gl_file.abspos + gl_file.relpos ) );\r
1454 \r
1455   return rc;\r
1456 } /* fn_seteof */\r
1457 \r
1458 \r
1459 \r
1460 \r
1461 /****************************************************************************\r
1462  *\r
1463  * fn_truncate\r
1464  *\r
1465  * Open a file and set end of file\r
1466  *\r
1467  * INPUT:       filename - name of the file\r
1468  *              filesize - required new size\r
1469  * RETURN:      NULL on error, otherwise file pointer\r
1470  *\r
1471  ***************************************************************************/\r
1472 F_FILE * fn_truncate ( const char * filename, long filesize )\r
1473 {\r
1474   F_FILE       * f = fn_open( filename, "r+" );\r
1475   unsigned char  rc;\r
1476 \r
1477   if ( f != NULL )\r
1478   {\r
1479     rc = _f_fseek( (long)gl_file.filesize );\r
1480     if ( rc == F_NO_ERROR )\r
1481     {\r
1482       rc = _f_seteof( f, filesize );\r
1483     }\r
1484 \r
1485     if ( rc )\r
1486     {\r
1487       fn_close( f );\r
1488       f = NULL;\r
1489     }\r
1490   }\r
1491 \r
1492   return f;\r
1493 } /* fn_truncate */\r
1494 \r