Rev 363 | Rev 378 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 363 | Rev 368 | ||
---|---|---|---|
Line 52... | Line 52... | ||
52 | // + *) The territory aspect only refers to the place where the Software is used, not its programmed range. |
52 | // + *) The territory aspect only refers to the place where the Software is used, not its programmed range. |
53 | // + #### END OF LICENSING TERMS #### |
53 | // + #### END OF LICENSING TERMS #### |
54 | // + Note: For information on license extensions (e.g. commercial use), please contact us at info(@)hisystems.de. |
54 | // + Note: For information on license extensions (e.g. commercial use), please contact us at info(@)hisystems.de. |
55 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
55 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
56 | #include <stdio.h> |
56 | #include <stdio.h> |
57 | #include <string.h> |
- | |
58 | #include "91x_lib.h" |
57 | #include "91x_lib.h" |
59 | #include "timer1.h" |
58 | #include "timer1.h" |
60 | #include "fat16.h" |
59 | #include "fat16.h" |
61 | #include "sdc.h" |
60 | #include "sdc.h" |
62 | #include "uart1.h" |
61 | #include "uart1.h" |
63 | #include "main.h" |
62 | #include "logging.h" |
64 | - | ||
65 | //________________________________________________________________________________________________________________________________________ |
63 | //________________________________________________________________________________________________________________________________________ |
66 | // Module name: fat16.c |
64 | // Module name: fat16.c |
67 | // Compiler used: avr-gcc 3.4.5 |
65 | // Compiler used: avr-gcc 3.4.5 |
68 | // Last Modifikation: 20.03.2010 |
66 | // Last Modifikation: 20.03.2010 |
69 | // Version: 2.10 |
67 | // Version: 2.10 |
Line 105... | Line 103... | ||
105 | Root Directory Entry Start + # of Reserved + (# of Sectors Per FAT * 2) |
103 | Root Directory Entry Start + # of Reserved + (# of Sectors Per FAT * 2) |
106 | Data Area (Starts with Cluster #2) Start + # of Reserved + (# of Sectors Per FAT * 2) + ((Maximum Root Directory Entries * 32) / Bytes per Sector) |
104 | Data Area (Starts with Cluster #2) Start + # of Reserved + (# of Sectors Per FAT * 2) + ((Maximum Root Directory Entries * 32) / Bytes per Sector) |
107 | */ |
105 | */ |
Line 108... | Line -... | ||
108 | - | ||
109 | 106 | ||
110 | 107 | ||
Line 111... | Line 108... | ||
111 | /* |
108 | /* |
112 | ________________________________________________________________________________________________________________________________________ |
109 | ________________________________________________________________________________________________________________________________________ |
Line 126... | Line 123... | ||
126 | u16 EndCylSec; // End of Partition - Cylinder/Sector |
123 | u16 EndCylSec; // End of Partition - Cylinder/Sector |
127 | u32 NoSectorsBeforePartition; // Number of Sectors between the MBR and the First Sector in the Partition |
124 | u32 NoSectorsBeforePartition; // Number of Sectors between the MBR and the First Sector in the Partition |
128 | u32 NoSectorsPartition ; // Number of Sectors in the Partition |
125 | u32 NoSectorsPartition ; // Number of Sectors in the Partition |
129 | } __attribute__((packed)) PartitionEntry_t; |
126 | } __attribute__((packed)) PartitionEntry_t; |
Line 130... | Line -... | ||
130 | - | ||
131 | - | ||
132 | 127 | ||
133 | /* |
128 | /* |
Line 134... | Line 129... | ||
134 | Coding of Cylinder/Sector words |
129 | Coding of Cylinder/Sector words |
135 | 130 | ||
Line 311... | Line 306... | ||
311 | u16 SectorsPerFat; // how many sectors does a fat16 contain? |
306 | u16 SectorsPerFat; // how many sectors does a fat16 contain? |
312 | u32 FirstFatSector; // sector of the start of the fat |
307 | u32 FirstFatSector; // sector of the start of the fat |
313 | u32 FirstRootDirSector; // sector of the rootdirectory |
308 | u32 FirstRootDirSector; // sector of the rootdirectory |
314 | u32 FirstDataSector; // sector of the first cluster containing data (cluster2). |
309 | u32 FirstDataSector; // sector of the first cluster containing data (cluster2). |
315 | u32 LastDataSector; // the last data sector of the partition |
310 | u32 LastDataSector; // the last data sector of the partition |
316 | u8 VolumeLabel[12]; // the volume label |
311 | u8 VolumeLabel[12]; // the volume label |
317 | u32 CurrentWorkingDirectory;// A pointer to the directory we are actual using |
- | |
318 | s8 PathToCwd[256]; // a string containing the complete path to the current working directory |
- | |
319 | } __attribute__((packed)) Partition_t; |
312 | } Partition_t; |
Line 320... | Line 313... | ||
320 | 313 | ||
Line 321... | Line 314... | ||
321 | Partition_t Partition; // Structure holds partition information |
314 | Partition_t Partition; // Structure holds partition information |
Line 322... | Line -... | ||
322 | - | ||
323 | File_t FilePointer[FILE_MAX_OPEN]; // Allocate Memmoryspace for each filepointer used. |
- | |
324 | 315 | ||
325 | 316 | File_t FilePointer[FILE_MAX_OPEN]; // Allocate Memmoryspace for each filepointer used. |
|
326 | 317 | ||
327 | 318 | ||
328 | /****************************************************************************************************************************************/ |
319 | /****************************************************************************************************************************************/ |
Line 438... | Line 429... | ||
438 | 429 | ||
439 | // search subpath from beginning of filepath |
430 | // search subpath from beginning of filepath |
440 | subpath = NULL; |
431 | subpath = NULL; |
441 | readpointer = 0; |
432 | readpointer = 0; |
442 | if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
433 | if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
443 | while(subpath == NULL && (SD_WatchDog)) // search the filepath until a subpath was found. |
434 | while(subpath == NULL) // search the filepath until a subpath was found. |
444 | { |
435 | { |
445 | if(((filepath[readpointer] == 0) || (filepath[readpointer] == '/'))) // if '/' found or end of filepath reached |
436 | if(((filepath[readpointer] == 0) || (filepath[readpointer] == '/'))) // if '/' found or end of filepath reached |
446 | { |
437 | { |
447 | subpath = (s8*)&filepath[readpointer]; // store the position of the first "/" found after the beginning of the filenpath |
438 | subpath = (s8*)&filepath[readpointer]; // store the position of the first "/" found after the beginning of the filenpath |
Line 450... | Line 441... | ||
450 | } |
441 | } |
Line 451... | Line 442... | ||
451 | 442 | ||
452 | // clear dirname with spaces |
443 | // clear dirname with spaces |
453 | dirname[11] = 0; // terminate dirname |
444 | dirname[11] = 0; // terminate dirname |
454 | for(writepointer = 0; writepointer < 11; writepointer++) dirname[writepointer] = ' '; |
- | |
455 | - | ||
456 | // handle the special dirnames "." and ".." seperately |
- | |
457 | readpointer = 0; |
- | |
458 | if(filepath[0] == '/') readpointer++; |
- | |
459 | // if we are trying to enter directories "." or ".." |
- | |
460 | if(filepath[readpointer] == '.') |
- | |
461 | { |
- | |
462 | // directory '.' |
- | |
463 | if(filepath[readpointer+1] == 0) |
- | |
464 | { |
- | |
465 | dirname[0] = '.'; |
- | |
466 | return((s8*)&filepath[readpointer]); |
- | |
467 | } |
- | |
468 | // directory '..' |
- | |
469 | if((filepath[readpointer+1] == '.') && (filepath[readpointer+2] == 0)) |
- | |
470 | { |
- | |
471 | dirname[0] = '.'; |
- | |
472 | dirname[1] = '.'; |
- | |
473 | return((s8*)&filepath[readpointer]); |
- | |
474 | } |
- | |
475 | } |
- | |
476 | 445 | for(writepointer = 0; writepointer < 11; writepointer++) dirname[writepointer] = ' '; |
|
477 | writepointer = 0; |
446 | writepointer = 0; |
478 | // start seperating the dirname from the filepath. |
447 | // start seperating the dirname from the filepath. |
479 | readpointer = 0; |
448 | readpointer = 0; |
480 | if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
449 | if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
481 | while( &filepath[readpointer] < subpath && (SD_WatchDog)) |
450 | while( &filepath[readpointer] < subpath) |
482 | { |
451 | { |
483 | if(writepointer >= 11) return(NULL); // dirname to long |
452 | if(writepointer >= 11) return(NULL); // dirname to long |
484 | if(filepath[readpointer] == '.') // seperating dirname and extension. |
453 | if(filepath[readpointer] == '.') // seperating dirname and extension. |
485 | { |
454 | { |
Line 577... | Line 546... | ||
577 | } |
546 | } |
578 | returnvalue = SDC_Deinit(); // uninitialize interface to sd-card |
547 | returnvalue = SDC_Deinit(); // uninitialize interface to sd-card |
579 | Partition.IsValid = 0; // mark data in partition structure as invalid |
548 | Partition.IsValid = 0; // mark data in partition structure as invalid |
580 | Partition.VolumeLabel[0]='\0'; |
549 | Partition.VolumeLabel[0]='\0'; |
581 | UART1_PutString("ok"); |
550 | UART1_PutString("ok"); |
- | 551 | SD_LoggingError = 100; |
|
582 | return(returnvalue); |
552 | return(returnvalue); |
583 | } |
553 | } |
Line 584... | Line 554... | ||
584 | 554 | ||
585 | /****************************************************************************************************************************************/ |
555 | /****************************************************************************************************************************************/ |
Line 599... | Line 569... | ||
599 | File_t *file; |
569 | File_t *file; |
600 | u8 result = 0; |
570 | u8 result = 0; |
Line 601... | Line 571... | ||
601 | 571 | ||
602 | UART1_PutString("\r\n FAT16 init..."); |
572 | UART1_PutString("\r\n FAT16 init..."); |
- | 573 | Partition.IsValid = 0; |
|
603 | Partition.IsValid = 0; |
574 | |
604 | // declare the filepointers as unused. |
575 | // declare the filepointers as unused. |
605 | for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
576 | for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
606 | { |
577 | { |
607 | UnlockFilePointer(&FilePointer[cnt]); |
578 | UnlockFilePointer(&FilePointer[cnt]); |
Line 681... | Line 652... | ||
681 | UART1_PutString("VBR: Partition ist not FAT16 type."); |
652 | UART1_PutString("VBR: Partition ist not FAT16 type."); |
682 | result = 6; |
653 | result = 6; |
683 | goto end; |
654 | goto end; |
684 | } |
655 | } |
685 | Partition.IsValid = 1; // mark data in partition structure as valid |
656 | Partition.IsValid = 1; // mark data in partition structure as valid |
686 | Partition.CurrentWorkingDirectory = Partition.FirstRootDirSector; |
- | |
687 | strcpy(Partition.PathToCwd,"/"); |
- | |
688 | result = 0; |
657 | result = 0; |
689 | end: |
658 | end: |
690 | if(result != 0) Fat16_Deinit(); |
659 | if(result != 0) Fat16_Deinit(); |
691 | else UART1_PutString("ok"); |
660 | else UART1_PutString("ok"); |
692 | return(result); |
661 | return(result); |
Line 714... | Line 683... | ||
714 | { |
683 | { |
715 | file->SectorInCache = file->FirstSectorOfCurrCluster + i; |
684 | file->SectorInCache = file->FirstSectorOfCurrCluster + i; |
716 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) |
685 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) |
717 | { |
686 | { |
718 | Fat16_Deinit(); |
687 | Fat16_Deinit(); |
- | 688 | retvalue = 0; |
|
719 | return(0); |
689 | return(retvalue); |
720 | } |
690 | } |
721 | } |
691 | } |
722 | return(retvalue); |
692 | return(retvalue); |
723 | } |
693 | } |
Line 753... | Line 723... | ||
753 | { |
723 | { |
754 | file->SectorInCache = sector; // update sector stored in buffer |
724 | file->SectorInCache = sector; // update sector stored in buffer |
755 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card |
725 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card |
756 | { |
726 | { |
757 | Fat16_Deinit(); |
727 | Fat16_Deinit(); |
758 | return (CLUSTER_UNDEFINED); |
728 | return (cluster); |
759 | } |
729 | } |
760 | } |
730 | } |
761 | // read the next cluster from cache |
731 | // read the next cluster from cache |
762 | fat = (Fat16Entry_t *)(&(file->Cache[byte])); |
732 | fat = (Fat16Entry_t *)(&(file->Cache[byte])); |
763 | cluster = fat->NextCluster; |
733 | cluster = fat->NextCluster; |
Line 775... | Line 745... | ||
775 | } |
745 | } |
776 | return(cluster); |
746 | return(cluster); |
777 | } |
747 | } |
Line 778... | Line -... | ||
778 | - | ||
779 | 748 | ||
780 | 749 | ||
781 | /****************************************************************************************************************************************/ |
750 | /****************************************************************************************************************************************/ |
782 | /* Function: FindNextFreeCluster(File_t *); */ |
751 | /* Function: FindNextFreeCluster(File_t *); */ |
783 | /* */ |
752 | /* */ |
Line 802... | Line 771... | ||
802 | curr_sector = Partition.FirstFatSector + fat_sector; // calculate sector to read |
771 | curr_sector = Partition.FirstFatSector + fat_sector; // calculate sector to read |
803 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
772 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
804 | if( SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector of fat from sd-card. |
773 | if( SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector of fat from sd-card. |
805 | { |
774 | { |
806 | Fat16_Deinit(); |
775 | Fat16_Deinit(); |
807 | return(CLUSTER_UNDEFINED); |
776 | return(free_cluster); |
808 | } |
777 | } |
Line 809... | Line 778... | ||
809 | 778 | ||
Line 810... | Line 779... | ||
810 | fat = (Fat16Entry_t *)file->Cache; // set fat pointer to file cache |
779 | fat = (Fat16Entry_t *)file->Cache; // set fat pointer to file cache |
Line 815... | Line 784... | ||
815 | { |
784 | { |
816 | fat[fat_entry].NextCluster = FAT16_CLUSTER_LAST_MAX; // mark this fat-entry as used |
785 | fat[fat_entry].NextCluster = FAT16_CLUSTER_LAST_MAX; // mark this fat-entry as used |
817 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // and save the sector at the sd-card. |
786 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // and save the sector at the sd-card. |
818 | { |
787 | { |
819 | Fat16_Deinit(); |
788 | Fat16_Deinit(); |
820 | return(CLUSTER_UNDEFINED); |
789 | return(free_cluster); |
821 | } |
790 | } |
822 | free_cluster = (u16)(fat_sector * FAT16_ENTRIES_PER_SECTOR + (u32)fat_entry); |
791 | free_cluster = (u16)(fat_sector * FAT16_ENTRIES_PER_SECTOR + (u32)fat_entry); |
823 | fat_entry = FAT16_ENTRIES_PER_SECTOR; // terminate the search for a free cluster in this sector. |
792 | fat_entry = FAT16_ENTRIES_PER_SECTOR; // terminate the search for a free cluster in this sector. |
824 | } |
793 | } |
825 | } |
794 | } |
826 | fat_sector++; // continue the search in next fat sector |
795 | fat_sector++; // continue the search in next fat sector |
827 | // repeat until the end of the fat is reached and no free cluster has been found so far |
796 | // repeat until the end of the fat is reached and no free cluster has been found so far |
828 | }while((fat_sector < Partition.SectorsPerFat) && (!free_cluster) && (SD_WatchDog)); |
797 | }while((fat_sector < Partition.SectorsPerFat) && (!free_cluster)); |
829 | return(free_cluster); |
798 | return(free_cluster); |
830 | } |
799 | } |
Line 831... | Line 800... | ||
831 | 800 | ||
Line 865... | Line 834... | ||
865 | file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
834 | file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
866 | file->SectorOfCurrCluster = 0; |
835 | file->SectorOfCurrCluster = 0; |
867 | file->ByteOfCurrSector = 0; |
836 | file->ByteOfCurrSector = 0; |
868 | file->Position = 0; |
837 | file->Position = 0; |
869 | if(file->FirstSectorOfCurrCluster == SECTOR_UNDEFINED) return(retvalue); |
838 | if(file->FirstSectorOfCurrCluster == SECTOR_UNDEFINED) return(retvalue); |
870 | while(file->Position < fposition && (SD_WatchDog)) // repeat until the current position is less than target |
839 | while(file->Position < fposition) // repeat until the current position is less than target |
871 | { |
840 | { |
872 | file->Position++; // increment file position |
841 | file->Position++; // increment file position |
873 | file->ByteOfCurrSector++; // next byte in current sector |
842 | file->ByteOfCurrSector++; // next byte in current sector |
874 | if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) |
843 | if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) |
875 | { |
844 | { |
Line 955... | Line 924... | ||
955 | Fat16_Deinit(); |
924 | Fat16_Deinit(); |
956 | return(0); |
925 | return(0); |
957 | } |
926 | } |
958 | } |
927 | } |
959 | } |
928 | } |
960 | while(repeat && (SD_WatchDog)); |
929 | while(repeat); |
Line 961... | Line 930... | ||
961 | 930 | ||
962 | return 1; |
931 | return 1; |
Line 1066... | Line 1035... | ||
1066 | if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(direntry_exist); |
1035 | if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(direntry_exist); |
Line 1067... | Line 1036... | ||
1067 | 1036 | ||
1068 | // dir entries can be searched only in filesclusters that have |
1037 | // dir entries can be searched only in filesclusters that have |
1069 | // a corresponding dir entry with adir-flag set in its attribute |
1038 | // a corresponding dir entry with adir-flag set in its attribute |
- | 1039 | // or direct within the root directory area |
|
1070 | // or direct within the root directory area |
1040 | |
1071 | file->FirstSectorOfFirstCluster = SECTOR_UNDEFINED; |
1041 | file->FirstSectorOfFirstCluster = SECTOR_UNDEFINED; |
1072 | // no current directory exist therefore assume searching in the root |
1042 | // no current directory exist therefore assume searching in the root |
1073 | if(file->DirectorySector == SECTOR_UNDEFINED) |
1043 | if(file->DirectorySector == SECTOR_UNDEFINED) |
1074 | { |
1044 | { |
Line 1092... | Line 1062... | ||
1092 | // check if the directory entry of current file is existent and has the dir-flag set |
1062 | // check if the directory entry of current file is existent and has the dir-flag set |
1093 | file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
1063 | file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
1094 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1064 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1095 | { |
1065 | { |
1096 | Fat16_Deinit(); |
1066 | Fat16_Deinit(); |
1097 | return(0); |
1067 | return(direntry_exist); |
1098 | } |
1068 | } |
1099 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1069 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1100 | switch((u8)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
1070 | switch((u8)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
1101 | { |
1071 | { |
1102 | case SLOT_EMPTY: |
1072 | case SLOT_EMPTY: |
Line 1120... | Line 1090... | ||
1120 | file->SectorOfCurrCluster = 0; |
1090 | file->SectorOfCurrCluster = 0; |
1121 | file->ByteOfCurrSector = 0; |
1091 | file->ByteOfCurrSector = 0; |
Line 1122... | Line 1092... | ||
1122 | 1092 | ||
1123 | do // loop over all data clusters of the current directory entry |
1093 | do // loop over all data clusters of the current directory entry |
1124 | { |
1094 | { |
1125 | dir_sector = 0; |
1095 | dir_sector = 0; // reset sector counter within a new cluster |
1126 | do // loop over all sectors of a cluster or all sectors of the root directory |
1096 | do // loop over all sectors of a cluster or all sectors of the root directory |
1127 | { |
1097 | { |
1128 | curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
1098 | curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
1129 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
1099 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
1130 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector |
1100 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector |
1131 | { |
1101 | { |
1132 | Fat16_Deinit(); |
1102 | Fat16_Deinit(); |
1133 | return(0); |
1103 | return(direntry_exist); |
1134 | } |
1104 | } |
1135 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1105 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1136 | // search all directory entries within that sector |
1106 | // search all directory entries within that sector |
1137 | for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
1107 | for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
Line 1162... | Line 1132... | ||
1162 | dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
1132 | dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
1163 | } // end of first byte of name check |
1133 | } // end of first byte of name check |
1164 | } |
1134 | } |
1165 | dir_sector++; // search next sector |
1135 | dir_sector++; // search next sector |
1166 | // stop if we reached the end of the cluster or the end of the root dir |
1136 | // stop if we reached the end of the cluster or the end of the root dir |
1167 | }while((dir_sector < max_dir_sector) && (!direntry_exist) && (SD_WatchDog)); |
1137 | }while((dir_sector < max_dir_sector) && (!direntry_exist)); |
Line 1168... | Line 1138... | ||
1168 | 1138 | ||
1169 | // if we are seaching in the data area and the file not found in this cluster so take next cluster. |
1139 | // if we are seaching in the data area and the file not found in this cluster so take next cluster. |
1170 | if(!direntry_exist && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
1140 | if(!direntry_exist && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
1171 | { |
1141 | { |
1172 | end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
1142 | end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
1173 | } |
1143 | } |
1174 | }while((end_of_directory_not_reached) && (!direntry_exist) && (SD_WatchDog)); // repeat until a next cluster exist an no |
1144 | }while((end_of_directory_not_reached) && (!direntry_exist)); // repeat until a next cluster exist an no |
1175 | return(direntry_exist); |
1145 | return(direntry_exist); |
Line 1176... | Line 1146... | ||
1176 | } |
1146 | } |
Line 1225... | Line 1195... | ||
1225 | // check if the directory entry of current file is existent and has the dir-flag set |
1195 | // check if the directory entry of current file is existent and has the dir-flag set |
1226 | file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
1196 | file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
1227 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1197 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1228 | { |
1198 | { |
1229 | Fat16_Deinit(); |
1199 | Fat16_Deinit(); |
1230 | return(0); |
1200 | return(retvalue); |
1231 | } |
1201 | } |
1232 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1202 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1233 | switch((u8)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
1203 | switch((u8)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
1234 | { |
1204 | { |
1235 | case SLOT_EMPTY: |
1205 | case SLOT_EMPTY: |
Line 1267... | Line 1237... | ||
1267 | curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
1237 | curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
1268 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
1238 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
1269 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1239 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1270 | { |
1240 | { |
1271 | Fat16_Deinit(); |
1241 | Fat16_Deinit(); |
1272 | return(0); |
1242 | return(retvalue); |
1273 | } |
1243 | } |
Line 1274... | Line 1244... | ||
1274 | 1244 | ||
1275 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1245 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
1276 | // search all directory entries of a sector |
1246 | // search all directory entries of a sector |
Line 1293... | Line 1263... | ||
1293 | dir[dir_entry].StartCluster = subdircluster; // copy the location of the first datacluster to the directoryentry. |
1263 | dir[dir_entry].StartCluster = subdircluster; // copy the location of the first datacluster to the directoryentry. |
1294 | dir[dir_entry].Size = 0; // the new createted file has no content yet. |
1264 | dir[dir_entry].Size = 0; // the new createted file has no content yet. |
1295 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // write back to card |
1265 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // write back to card |
1296 | { |
1266 | { |
1297 | Fat16_Deinit(); |
1267 | Fat16_Deinit(); |
1298 | return(0); |
1268 | return(retvalue); |
1299 | } |
1269 | } |
1300 | file->FirstSectorOfFirstCluster = Fat16ClusterToSector(subdircluster); // Calculate absolute sectorposition of first datacluster. |
1270 | file->FirstSectorOfFirstCluster = Fat16ClusterToSector(subdircluster); // Calculate absolute sectorposition of first datacluster. |
1301 | file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; // Start reading the file with the first sector of the first datacluster. |
1271 | file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; // Start reading the file with the first sector of the first datacluster. |
1302 | file->SectorOfCurrCluster = 0; // reset sector of cureen cluster |
1272 | file->SectorOfCurrCluster = 0; // reset sector of cureen cluster |
1303 | file->ByteOfCurrSector = 0; // reset the byte location within the current sector |
1273 | file->ByteOfCurrSector = 0; // reset the byte location within the current sector |
Line 1311... | Line 1281... | ||
1311 | ClearCurrCluster(file); // fill cluster with zeros |
1281 | ClearCurrCluster(file); // fill cluster with zeros |
1312 | file->SectorInCache = file->FirstSectorOfFirstCluster; |
1282 | file->SectorInCache = file->FirstSectorOfFirstCluster; |
1313 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1283 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
1314 | { |
1284 | { |
1315 | Fat16_Deinit(); |
1285 | Fat16_Deinit(); |
1316 | return(0); |
1286 | return(retvalue); |
1317 | } |
1287 | } |
1318 | dir = (DirEntry_t *)file->Cache; |
1288 | dir = (DirEntry_t *)file->Cache; |
1319 | // create direntry "." to current dir |
1289 | // create direntry "." to current dir |
1320 | dir[0].Name[0] = 0x2E; |
1290 | dir[0].Name[0] = 0x2E; |
1321 | for(i = 1; i < 11; i++) dir[0].Name[i] = ' '; |
1291 | for(i = 1; i < 11; i++) dir[0].Name[i] = ' '; |
Line 1330... | Line 1300... | ||
1330 | dir[1].StartCluster = dircluster; |
1300 | dir[1].StartCluster = dircluster; |
1331 | dir[1].Size = 0; |
1301 | dir[1].Size = 0; |
1332 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// read in the sector. |
1302 | if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// read in the sector. |
1333 | { |
1303 | { |
1334 | Fat16_Deinit(); |
1304 | Fat16_Deinit(); |
1335 | return(0); |
1305 | return(retvalue); |
1336 | } |
1306 | } |
1337 | } |
1307 | } |
1338 | retvalue = 1; |
1308 | retvalue = 1; |
1339 | dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
1309 | dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
1340 | } |
1310 | } |
1341 | } |
1311 | } |
1342 | dir_sector++; // search next sector |
1312 | dir_sector++; // search next sector |
1343 | // stop if we reached the end of the cluster or the end of the root dir |
1313 | // stop if we reached the end of the cluster or the end of the root dir |
1344 | }while((dir_sector < max_dir_sector) && (!retvalue) && (SD_WatchDog)); |
1314 | }while((dir_sector < max_dir_sector) && (!retvalue)); |
Line 1345... | Line 1315... | ||
1345 | 1315 | ||
1346 | // if we are seaching in the data area and the file not found in this cluster so take next cluster. |
1316 | // if we are seaching in the data area and the file not found in this cluster so take next cluster. |
1347 | if(!retvalue && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
1317 | if(!retvalue && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
1348 | { |
1318 | { |
1349 | end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
1319 | end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
1350 | } |
1320 | } |
1351 | }while((end_of_directory_not_reached) && (!retvalue) && (SD_WatchDog)); |
1321 | }while((end_of_directory_not_reached) && (!retvalue)); |
1352 | // Perhaps we are at the end of the last cluster of a directory file and have no free direntry found. |
1322 | // Perhaps we are at the end of the last cluster of a directory file and have no free direntry found. |
1353 | // Then we would need to add a cluster to that file and create the new direntry there. |
1323 | // Then we would need to add a cluster to that file and create the new direntry there. |
1354 | // This code is not implemented yet, because its occurs only if more that 32*32=1024 direntries are |
1324 | // This code is not implemented yet, because its occurs only if more that 32*32=1024 direntries are |
Line 1378... | Line 1348... | ||
1378 | 1348 | ||
1379 | // trace along the filepath |
1349 | // trace along the filepath |
1380 | path = (s8*)filename; // start a the beginning of the filename string |
1350 | path = (s8*)filename; // start a the beginning of the filename string |
1381 | file->DirectorySector = 0; // start at RootDirectory with file search |
1351 | file->DirectorySector = 0; // start at RootDirectory with file search |
1382 | file->DirectoryIndex = 0; |
- | |
1383 | /* if a path begins with a '/' at index 0 the search starts at the rootdirectory otherwise we will start relative to the cwd */ |
- | |
1384 | if(path[0] != '/') |
- | |
1385 | { |
- | |
1386 | /* is the current working directory the rootdirectory? */ |
- | |
1387 | if(Partition.CurrentWorkingDirectory == Partition.FirstRootDirSector) file->DirectorySector = 0; |
- | |
1388 | /* otherwise we are working in an subdirectory */ |
- | |
1389 | else file->DirectorySector = Partition.CurrentWorkingDirectory; |
- | |
1390 | } |
1352 | file->DirectoryIndex = 0; |
1391 | // as long as the file was not found and the remaining path is not empty |
1353 | // as long as the file was not found and the remaining path is not empty |
1392 | while((*path != 0) && !file_exist && (SD_WatchDog)) |
1354 | while((*path != 0) && !file_exist) |
1393 | { // separate dirname and subpath from filepath string |
1355 | { // separate dirname and subpath from filepath string |
1394 | subpath = SeperateDirName(path, dirname); |
1356 | subpath = SeperateDirName(path, dirname); |
1395 | if(subpath != NULL) |
1357 | if(subpath != NULL) |
1396 | { |
1358 | { |
Line 1449... | Line 1411... | ||
1449 | // trace along the filepath |
1411 | // trace along the filepath |
1450 | path = (s8*)filename; // start a the beginning of the filename string |
1412 | path = (s8*)filename; // start a the beginning of the filename string |
1451 | file->DirectorySector = 0; // start at RootDirectory with file search |
1413 | file->DirectorySector = 0; // start at RootDirectory with file search |
1452 | file->DirectoryIndex = 0; |
1414 | file->DirectoryIndex = 0; |
1453 | // as long as the file was not created and the remaining file path is not empty |
1415 | // as long as the file was not created and the remaining file path is not empty |
1454 | while((*path != 0) && !file_created && (SD_WatchDog)) |
1416 | while((*path != 0) && !file_created) |
1455 | { // separate dirname and subpath from filepath string |
1417 | { // separate dirname and subpath from filepath string |
1456 | subpath = SeperateDirName(path, dirname); |
1418 | subpath = SeperateDirName(path, dirname); |
1457 | if(subpath != NULL) |
1419 | if(subpath != NULL) |
1458 | { |
1420 | { |
1459 | if(*subpath == 0) |
1421 | if(*subpath == 0) |
Line 1496... | Line 1458... | ||
1496 | /* Returnvalue: The filepointer to the file or 0 if faild. */ |
1458 | /* Returnvalue: The filepointer to the file or 0 if faild. */ |
1497 | /********************************************************************************************************************************************/ |
1459 | /********************************************************************************************************************************************/ |
1498 | File_t * fopen_(s8 * const filename, const s8 mode) |
1460 | File_t * fopen_(s8 * const filename, const s8 mode) |
1499 | { |
1461 | { |
1500 | File_t *file = 0; |
1462 | File_t *file = 0; |
1501 | s8 *cptr; |
- | |
Line 1502... | Line 1463... | ||
1502 | 1463 | ||
Line 1503... | Line 1464... | ||
1503 | if((!Partition.IsValid) || (filename == 0)) return(file); |
1464 | if((!Partition.IsValid) || (filename == 0)) return(file); |
1504 | 1465 | ||
Line 1519... | Line 1480... | ||
1519 | file->SectorInCache = SECTOR_UNDEFINED; // the last sector read, wich is still in the sectorbuffer. |
1480 | file->SectorInCache = SECTOR_UNDEFINED; // the last sector read, wich is still in the sectorbuffer. |
1520 | file->DirectorySector = SECTOR_UNDEFINED; // the sectorposition where the directoryentry has been made. |
1481 | file->DirectorySector = SECTOR_UNDEFINED; // the sectorposition where the directoryentry has been made. |
1521 | file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector. |
1482 | file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector. |
1522 | file->Attribute = 0; // the attribute of the file opened. |
1483 | file->Attribute = 0; // the attribute of the file opened. |
Line 1523... | Line -... | ||
1523 | - | ||
1524 | // bring the path into the correct syntax |
- | |
1525 | cptr = filename; |
- | |
1526 | // search the whole string |
- | |
1527 | while(*cptr != 0 && (SD_WatchDog)) |
- | |
1528 | { |
- | |
1529 | // replace all '\' by '/' |
- | |
1530 | if(*cptr == '\\') *cptr = '/'; |
- | |
1531 | cptr++; |
- | |
1532 | } |
1484 | |
1533 | // check if a real file (no directory) to the given filename exist |
1485 | // check if a real file (no directory) to the given filename exist |
1534 | if(FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file)) |
1486 | if(FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file)) |
1535 | { // file exist |
1487 | { // file exist |
1536 | switch(mode) // check mode |
1488 | switch(mode) // check mode |
Line 1605... | Line 1557... | ||
1605 | // try to create the file |
1557 | // try to create the file |
1606 | if(!FileCreate(filename, ATTR_ARCHIVE, file)) |
1558 | if(!FileCreate(filename, ATTR_ARCHIVE, file)) |
1607 | { // if it could not be created |
1559 | { // if it could not be created |
1608 | fclose_(file); |
1560 | fclose_(file); |
1609 | file = NULL; |
1561 | file = NULL; |
1610 | } |
1562 | } |
1611 | break; |
1563 | break; |
1612 | case 'r': // else opened for 'r' |
1564 | case 'r': // else opened for 'r' |
1613 | default: // if unsupported mode |
1565 | default: // if unsupported mode |
1614 | fclose_(file); |
1566 | fclose_(file); |
1615 | file = NULL; |
1567 | file = NULL; |
Line 1708... | Line 1660... | ||
1708 | s16 c = EOF; |
1660 | s16 c = EOF; |
1709 | u32 curr_sector; |
1661 | u32 curr_sector; |
Line 1710... | Line 1662... | ||
1710 | 1662 | ||
1711 | if( (!Partition.IsValid) || (file == NULL)) return(c); |
1663 | if( (!Partition.IsValid) || (file == NULL)) return(c); |
1712 | // if the end of the file is not reached, get the next character. |
1664 | // if the end of the file is not reached, get the next character. |
1713 | if((0 < file->Size) && ((file->Position) < file->Size) ) |
1665 | if((0 < file->Size) && ((file->Position+1) < file->Size) ) |
1714 | { |
1666 | { |
1715 | curr_sector = file->FirstSectorOfCurrCluster; // calculate the sector of the next character to be read. |
1667 | curr_sector = file->FirstSectorOfCurrCluster; // calculate the sector of the next character to be read. |
Line 1716... | Line 1668... | ||
1716 | curr_sector += file->SectorOfCurrCluster; |
1668 | curr_sector += file->SectorOfCurrCluster; |
1717 | 1669 | ||
1718 | if(file->SectorInCache != curr_sector) |
1670 | if(file->SectorInCache != curr_sector) |
1719 | { |
1671 | { |
1720 | file->SectorInCache = curr_sector; |
1672 | file->SectorInCache = curr_sector; |
1721 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache,file->Cache)) |
1673 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache,file->Cache)) |
1722 | { |
1674 | { |
1723 | Fat16_Deinit(); |
1675 | Fat16_Deinit(); |
1724 | return(EOF); |
1676 | return(c); |
1725 | } |
1677 | } |
1726 | } |
1678 | } |
1727 | c = (s16) file->Cache[file->ByteOfCurrSector]; |
1679 | c = (s16) file->Cache[file->ByteOfCurrSector]; |
Line 1846... | Line 1798... | ||
1846 | 1798 | ||
Line 1847... | Line 1799... | ||
1847 | if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
1799 | if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
Line 1848... | Line 1800... | ||
1848 | 1800 | ||
1849 | pbuff = (u8 *) buffer; // cast the void pointer to an u8 * |
1801 | pbuff = (u8 *) buffer; // cast the void pointer to an u8 * |
1850 | 1802 | ||
1851 | while((object_cnt < count) && success && (SD_WatchDog)) |
1803 | while((object_cnt < count) && success) |
1852 | { |
1804 | { |
1853 | object_size = size; |
1805 | object_size = size; |
1854 | while((size > 0) && success && (SD_WatchDog)) |
1806 | while((size > 0) && success) |
1855 | { |
1807 | { |
1856 | c = fgetc_(file); |
1808 | c = fgetc_(file); |
Line 1889... | Line 1841... | ||
1889 | 1841 | ||
1890 | if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
1842 | if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
1891 | if(file->Mode == 'r') return (0); // opened read only |
1843 | if(file->Mode == 'r') return (0); // opened read only |
Line 1892... | Line 1844... | ||
1892 | pbuff = (u8 *) buffer; // cast the void pointer to an u8 * |
1844 | pbuff = (u8 *) buffer; // cast the void pointer to an u8 * |
1893 | 1845 | ||
1894 | while((object_cnt < count) && success && (SD_WatchDog)) |
1846 | while((object_cnt < count) && success) |
1895 | { |
1847 | { |
1896 | object_size = size; |
1848 | object_size = size; |
1897 | while((size > 0) && success && (SD_WatchDog)) |
1849 | while((size > 0) && success) |
1898 | { |
1850 | { |
1899 | c = fputc_(*pbuff, file); // write a byte from the buffer to the opened file. |
1851 | c = fputc_(*pbuff, file); // write a byte from the buffer to the opened file. |
1900 | if(c != EOF) |
1852 | if(c != EOF) |
Line 1926... | Line 1878... | ||
1926 | u8 i=0; |
1878 | u8 i=0; |
1927 | s16 c = 0; |
1879 | s16 c = 0; |
Line 1928... | Line 1880... | ||
1928 | 1880 | ||
1929 | if((!Partition.IsValid) || (file == NULL) || (string == NULL)) return(EOF); |
1881 | if((!Partition.IsValid) || (file == NULL) || (string == NULL)) return(EOF); |
1930 | if(file->Mode == 'r') return(EOF); |
1882 | if(file->Mode == 'r') return(EOF); |
1931 | while((string[i] != 0)&& (c != EOF) && (SD_WatchDog)) |
1883 | while((string[i] != 0)&& (c != EOF)) |
1932 | { |
1884 | { |
1933 | c = fputc_(string[i], file); |
1885 | c = fputc_(string[i], file); |
1934 | i++; |
1886 | i++; |
1935 | } |
1887 | } |
Line 1949... | Line 1901... | ||
1949 | s16 c = 0, bytecount; |
1901 | s16 c = 0, bytecount; |
Line 1950... | Line 1902... | ||
1950 | 1902 | ||
1951 | if((!Partition.IsValid) || (file == NULL) || (string == NULL) || (length < 1)) return (0); |
1903 | if((!Partition.IsValid) || (file == NULL) || (string == NULL) || (length < 1)) return (0); |
1952 | bytecount = length; |
1904 | bytecount = length; |
1953 | pbuff = string; // set write pointer to start of string |
1905 | pbuff = string; // set write pointer to start of string |
1954 | while(bytecount > 1 && (SD_WatchDog)) // read the length-1 characters from the file to the string. |
1906 | while(bytecount > 1) // read the length-1 characters from the file to the string. |
1955 | { |
1907 | { |
1956 | c = fgetc_(file); // read a character from the opened file. |
1908 | c = fgetc_(file); // read a character from the opened file. |
1957 | switch(c) |
1909 | switch(c) |
1958 | { |
1910 | { |
Line 2050... | Line 2002... | ||
2050 | curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
2002 | curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
2051 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
2003 | file->SectorInCache = curr_sector; // upate the sector number of file cache. |
2052 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector |
2004 | if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector |
2053 | { |
2005 | { |
2054 | Fat16_Deinit(); |
2006 | Fat16_Deinit(); |
2055 | return(NULL); |
2007 | return(pVolumeLabel); |
2056 | } |
2008 | } |
2057 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
2009 | dir = (DirEntry_t *)file->Cache; // set pointer to directory |
2058 | // search all directory entries within that sector |
2010 | // search all directory entries within that sector |
2059 | for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
2011 | for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
2060 | { // check for existing dir entry |
2012 | { // check for existing dir entry |
Line 2082... | Line 2034... | ||
2082 | pVolumeLabel = Partition.VolumeLabel; |
2034 | pVolumeLabel = Partition.VolumeLabel; |
2083 | } // end of first byte of name check |
2035 | } // end of first byte of name check |
2084 | } |
2036 | } |
2085 | dir_sector++; // search next sector |
2037 | dir_sector++; // search next sector |
2086 | // stop if we reached the end of the cluster or the end of the root dir |
2038 | // stop if we reached the end of the cluster or the end of the root dir |
2087 | }while((dir_sector < max_dir_sector) && (!pVolumeLabel) && (SD_WatchDog)); |
2039 | }while((dir_sector < max_dir_sector) && (!pVolumeLabel)); |
Line 2088... | Line 2040... | ||
2088 | 2040 | ||
2089 | UnlockFilePointer(file); |
2041 | UnlockFilePointer(file); |
2090 | return(pVolumeLabel); |
2042 | return(pVolumeLabel); |
2091 | } |
- | |
2092 | - | ||
2093 | - | ||
2094 | - | ||
2095 | #define ATTR_NONE 0x00 // normal file |
- | |
2096 | #define ATTR_READONLY 0x01 // file is readonly |
- | |
2097 | #define ATTR_HIDDEN 0x02 // file is hidden |
- | |
2098 | #define ATTR_SYSTEM 0x04 // file is a system file |
- | |
2099 | #define ATTR_VOLUMELABEL 0x08 // entry is a volume label |
- | |
2100 | #define ATTR_LONG_FILENAME 0x0F // this is a long filename entry |
- | |
2101 | #define ATTR_SUBDIRECTORY 0x10 // entry is a directory name |
- | |
2102 | #define ATTR_ARCHIVE 0x20 // file is new or modified |
- | |
2103 | - | ||
2104 | - | ||
2105 | /********************************************************************************************************************************************/ |
- | |
2106 | /* Function: u8 FindItem(Find_t); */ |
- | |
2107 | /* */ |
- | |
2108 | /* Description: This function looks for the item specified by global structure FindElement in the actual directory */ |
- | |
2109 | /* */ |
- | |
2110 | /* */ |
- | |
2111 | /* Returnvalue: TRUE if an matching element was found */ |
- | |
2112 | /********************************************************************************************************************************************/ |
- | |
2113 | - | ||
2114 | u8 FindItem(Find_t *findelement) |
- | |
2115 | { |
- | |
2116 | u16 index = 0; |
- | |
2117 | u16 max_dir_sector = 0; |
- | |
2118 | u16 end_of_directory_not_reached = 1; // the directory has been read completely without a result. |
- | |
2119 | u8 i = 0; |
- | |
2120 | u8 readpointer = 0; |
- | |
2121 | u8 writepointer = 0; |
- | |
2122 | u8 retvalue = 0; |
- | |
2123 | DirEntry_t *DirectoryEntry; |
- | |
2124 | File_t file; |
- | |
2125 | - | ||
2126 | file.FirstSectorOfCurrCluster = findelement->fp.FirstSectorOfCurrCluster; |
- | |
2127 | file.SectorOfCurrCluster = findelement->fp.SectorOfCurrCluster; |
- | |
2128 | index = findelement->fp.DirectoryIndex; |
- | |
2129 | - | ||
2130 | // within the root directory area we can read sectors sequentially until the end of this area |
- | |
2131 | if((Partition.FirstRootDirSector <= file.FirstSectorOfCurrCluster) && (file.FirstSectorOfCurrCluster < Partition.FirstDataSector)) |
- | |
2132 | { |
- | |
2133 | max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
- | |
2134 | } |
- | |
2135 | // within the data clusters we can read sectors sequentially only within the cluster |
- | |
2136 | else if((Partition.FirstDataSector <= file.FirstSectorOfCurrCluster) && (file.FirstSectorOfCurrCluster <= Partition.LastDataSector)) |
- | |
2137 | { |
- | |
2138 | max_dir_sector = Partition.SectorsPerCluster; // limit max secters before next cluster |
- | |
2139 | } |
- | |
2140 | - | ||
2141 | do |
- | |
2142 | { // search the next 16 rootentries in this sector of the roordirectory. |
- | |
2143 | if(SD_SUCCESS != SDC_GetSector(((u32) file.FirstSectorOfCurrCluster + (u32)file.SectorOfCurrCluster), file.Cache)); // Read the Rootdirectory. |
- | |
2144 | { |
- | |
2145 | Fat16_Deinit(); |
- | |
2146 | return(0); |
- | |
2147 | } |
- | |
2148 | - | ||
2149 | DirectoryEntry = (DirEntry_t *)file.Cache; |
- | |
2150 | - | ||
2151 | while((!retvalue)&&(index<16) && (SD_WatchDog)) |
- | |
2152 | { |
- | |
2153 | i=0; |
- | |
2154 | if((u8) DirectoryEntry[index].Name[0] != 0xe5) // ignore deleted items. |
- | |
2155 | { |
- | |
2156 | while((i<=10)&&((DirectoryEntry[index].Name[i] == findelement->searchstring[i]) || (findelement->searchstring[i]=='*') || findelement->searchstring[i]=='?')) |
- | |
2157 | { |
- | |
2158 | i++; |
- | |
2159 | } |
- | |
2160 | } |
- | |
2161 | if((DirectoryEntry[index].Attribute <= 0x30) && (DirectoryEntry[index].Attribute & findelement->attribmask) && (i==11)) |
- | |
2162 | { |
- | |
2163 | for(readpointer=0;readpointer<=10;readpointer++) |
- | |
2164 | { |
- | |
2165 | if((DirectoryEntry[index].Name[readpointer] != ' ') && (readpointer!=8)) |
- | |
2166 | { |
- | |
2167 | findelement->name[writepointer] = DirectoryEntry[index].Name[readpointer]; // copy the name of the item found to the find_structure. |
- | |
2168 | writepointer++; |
- | |
2169 | } |
- | |
2170 | else |
- | |
2171 | { |
- | |
2172 | if(DirectoryEntry[index].Attribute == ATTR_ARCHIVE) |
- | |
2173 | { |
- | |
2174 | if(readpointer < 8) readpointer=8; |
- | |
2175 | if(DirectoryEntry[index].Name[readpointer] != ' ') |
- | |
2176 | { |
- | |
2177 | findelement->name[writepointer] = '.'; // then seperate the name and the extension by a '.' at index 8. |
- | |
2178 | writepointer++; |
- | |
2179 | findelement->name[writepointer] = DirectoryEntry[index].Name[readpointer]; // copy the name of the item found to the find_structure. |
- | |
2180 | writepointer++; |
- | |
2181 | } |
- | |
2182 | else break; |
- | |
2183 | } |
- | |
2184 | else break; |
- | |
2185 | } |
- | |
2186 | /* terminate the namestring with 0 for debugpurposes*/ |
- | |
2187 | findelement->name[12] = 0; |
- | |
2188 | } |
- | |
2189 | findelement->fp.FirstSectorOfFirstCluster = (u32) DirectoryEntry[index].StartCluster; |
- | |
2190 | findelement->fp.DirectoryIndex = index; |
- | |
2191 | findelement->fp.FirstSectorOfCurrCluster = file.FirstSectorOfCurrCluster; |
- | |
2192 | findelement->fp.DirectorySector = (file.FirstSectorOfCurrCluster + file.SectorOfCurrCluster); |
- | |
2193 | findelement->fp.SectorOfCurrCluster = file.SectorOfCurrCluster; |
- | |
2194 | findelement->fp.Size = DirectoryEntry[index].Size; |
- | |
2195 | findelement->fp.Attribute = DirectoryEntry[index].Attribute; |
- | |
2196 | retvalue = 1; |
- | |
2197 | } |
- | |
2198 | /* search the next sector */ |
- | |
2199 | index++; |
- | |
2200 | } |
- | |
2201 | /* this sector has been searched but we havn't found what we are looking for. Therefore we have to find the next sector */ |
- | |
2202 | if(!retvalue) // file not found in this sector so take next sector. |
- | |
2203 | { |
- | |
2204 | /* in the next sector we start looking for the specified entry beginning at index 0 */ |
- | |
2205 | index = 0; |
- | |
2206 | /* there are still sectors to be read within the cluster or within the linear addresspace of the rootdirectory */ |
- | |
2207 | if(file.SectorOfCurrCluster < max_dir_sector-1) file.SectorOfCurrCluster++; else end_of_directory_not_reached = 0; |
- | |
2208 | /* if we are looking for an directoryentry outside the rootdirectory and have reached the end of the cluster we have to get the next one */ |
- | |
2209 | if(Partition.FirstDataSector <= file.FirstSectorOfCurrCluster) |
- | |
2210 | { |
- | |
2211 | end_of_directory_not_reached = GetNextCluster(&file); |
- | |
2212 | } |
- | |
2213 | } |
- | |
2214 | } |
- | |
2215 | while((end_of_directory_not_reached) && (!retvalue) && (SD_WatchDog)); |
- | |
2216 | - | ||
2217 | return(retvalue); |
- | |
2218 | } |
- | |
2219 | - | ||
2220 | - | ||
2221 | - | ||
2222 | - | ||
2223 | /********************************************************************************************************************************************/ |
- | |
2224 | /* Function: findnext_(Find_t *); */ |
- | |
2225 | /* */ |
- | |
2226 | /* Description: This function looks for the next item in the specified directory with a matching filename and fileattributes specified */ |
- | |
2227 | /* by function findfirst() */ |
- | |
2228 | /* */ |
- | |
2229 | /* Returnvalue: */ |
- | |
2230 | /********************************************************************************************************************************************/ |
- | |
2231 | u8 findnext_(Find_t * findelement) |
- | |
2232 | { |
- | |
2233 | u8 itemfound = 0; |
- | |
2234 | u8 index = 0; |
- | |
2235 | - | ||
2236 | findelement->fp.DirectoryIndex++; |
- | |
2237 | - | ||
2238 | /* before we start searching an element we clear the complete namestring within the structure FindElement */ |
- | |
2239 | for(index=0;index<11;index++) findelement->name[index] = 0; |
- | |
2240 | - | ||
2241 | if(FindItem(findelement)) |
- | |
2242 | { |
- | |
2243 | itemfound = 1; |
- | |
2244 | } |
- | |
2245 | - | ||
2246 | return(itemfound); |
- | |
2247 | } |
- | |
2248 | - | ||
2249 | - | ||
2250 | - | ||
2251 | /********************************************************************************************************************************************/ |
- | |
2252 | /* Function: findfirst_(s8* filename, u8 attribmask, Find_t *); */ |
- | |
2253 | /* */ |
- | |
2254 | /* Description: This function looks for the first item in the specified directory with a matching filename and fileattributes */ |
- | |
2255 | /* The filename of the element found is transformed from 8.3 to a string */ |
- | |
2256 | /* */ |
- | |
2257 | /* */ |
- | |
2258 | /* Returnvalue: (1) if Element was found. (0) if no valid element was found */ |
- | |
2259 | /********************************************************************************************************************************************/ |
- | |
2260 | u8 findfirst_(const s8* name, u8 attribmask, Find_t *findelement) |
- | |
2261 | { |
- | |
2262 | u8 itemfound = 0; |
- | |
2263 | u8 index = 0; |
- | |
2264 | - | ||
2265 | /* initialize the FindElement structure */ |
- | |
2266 | findelement->fp.FirstSectorOfFirstCluster = 0; // First sector of the first cluster of the file. |
- | |
2267 | findelement->fp.FirstSectorOfCurrCluster = Partition.CurrentWorkingDirectory; // First sector of the cluster which is edited at the moment. |
- | |
2268 | findelement->fp.SectorOfCurrCluster = 0; // The sector within the current cluster. |
- | |
2269 | findelement->fp.ByteOfCurrSector = 0; // The byte location within the current sector. |
- | |
2270 | findelement->fp.Size = 0; // The size of the opend file in bytes. |
- | |
2271 | findelement->fp.Position = 0; // Pointer to a character within the file 0 < fileposition < filesize |
- | |
2272 | findelement->fp.DirectorySector = 0; // the sectorposition where the directoryentry has been made. |
- | |
2273 | findelement->fp.DirectoryIndex = 0; // The index to the directoryentry within the specified sector. |
- | |
2274 | findelement->attribfilter = 0; |
- | |
2275 | findelement->attribmask = attribmask; |
- | |
2276 | findelement->searchstring[0]=0; |
- | |
2277 | - | ||
2278 | /* seperate the name of the element to be found from the filepath and bring it to the 8.3*/ |
- | |
2279 | SeperateDirName(name, findelement->searchstring); |
- | |
2280 | /* after the name of the element is in 8.3 we process the wildcards (*). After an * all following character are wildcards to */ |
- | |
2281 | for(index=0;index<8;index++) |
- | |
2282 | { |
- | |
2283 | /* if we find an wildcard within the name of the searchstring all remaining character after the wildcard shall be wildcards also */ |
- | |
2284 | if(findelement->searchstring[index] == '*') |
- | |
2285 | { |
- | |
2286 | /* */ |
- | |
2287 | while(++index <8) findelement->searchstring[index] = '*'; |
- | |
2288 | } |
- | |
2289 | } |
- | |
2290 | for(index=8;index<11;index++) |
- | |
2291 | { |
- | |
2292 | /* if we find an wildcard within the name of the searchstring all remaining character after the wildcard shall be wildcards also */ |
- | |
2293 | if(findelement->searchstring[index] == '*') |
- | |
2294 | { |
- | |
2295 | /* */ |
- | |
2296 | while(++index <11) findelement->searchstring[index] = '*'; |
- | |
2297 | } |
- | |
2298 | } |
- | |
2299 | - | ||
2300 | /* the value of ...DirectoryIndex will be incremented in findnext_() thererfore it has to be decremented in findfirst_() */ |
- | |
2301 | findelement->fp.DirectoryIndex--; |
- | |
2302 | /* now lets search for the item within the direcory */ |
- | |
2303 | itemfound = findnext_(findelement); |
- | |
2304 | - | ||
2305 | return(itemfound); |
- | |
2306 | } |
- | |
2307 | - | ||
2308 | - | ||
2309 | /********************************************************************************************************************************************/ |
- | |
2310 | /* Function: u8 GetDirCount(s8* filepath); */ |
- | |
2311 | /* */ |
- | |
2312 | /* Description: This function counts the number of subdirectories the dirpath contains */ |
- | |
2313 | /* */ |
- | |
2314 | /* */ |
- | |
2315 | /* Returnvalue: then number of subdirectories within the specified path */ |
- | |
2316 | /********************************************************************************************************************************************/ |
- | |
2317 | u8 GetDirCount(u8 *dirpath) |
- | |
2318 | { |
- | |
2319 | u8 i=0; |
- | |
2320 | u8 cnt=0; |
- | |
2321 | - | ||
2322 | while(dirpath[i] != 0 && (SD_WatchDog)) |
- | |
2323 | { |
- | |
2324 | if(dirpath[i]=='/') |
- | |
2325 | { |
- | |
2326 | if(dirpath[i+1]!=0) cnt++; // ignore last'/' |
- | |
2327 | } |
- | |
2328 | i++; |
- | |
2329 | } |
- | |
2330 | i=0; |
- | |
2331 | return(cnt); |
- | |
2332 | } |
- | |
2333 | - | ||
2334 | - | ||
2335 | /********************************************************************************************************************************************/ |
- | |
2336 | /* Funtion: char *GetSubDirectory (char *dirpath, char *directory) */ |
- | |
2337 | /* */ |
- | |
2338 | /* Description: this function returns a pointer to the beginning of the next subdirectory or NULL */ |
- | |
2339 | /* */ |
- | |
2340 | /* */ |
- | |
2341 | /* returnvalue: number of subdirectories in the filepath */ |
- | |
2342 | /********************************************************************************************************************************************/ |
- | |
2343 | u8 * GetSubDirectory(u8 *dirpath, u8 *directory) |
- | |
2344 | { |
- | |
2345 | u8 *cptr = dirpath; |
- | |
2346 | u8 *dptr = directory; |
- | |
2347 | u8 *retvalue = NULL; |
- | |
2348 | - | ||
2349 | /* if the first character of the path is an '/' we go to the next character */ |
- | |
2350 | if(*cptr == '/') cptr++; |
- | |
2351 | /* search end of path or subdirectory*/ |
- | |
2352 | while((*cptr != 0) && (*cptr != '/') && (SD_WatchDog)) |
- | |
2353 | { |
- | |
2354 | *dptr = *cptr; |
- | |
2355 | dptr++; |
- | |
2356 | cptr++; |
- | |
2357 | } |
- | |
2358 | if(*cptr!=0) retvalue = ++cptr; |
- | |
2359 | *dptr = 0; |
- | |
2360 | - | ||
2361 | return(retvalue); |
- | |
2362 | } |
- | |
2363 | - | ||
2364 | /********************************************************************************************************************************************/ |
- | |
2365 | /* Function: s8 *GetPath(void); */ |
- | |
2366 | /* */ |
- | |
2367 | /* Description: This function function returns a pointer to the absolute path of the active partition */ |
- | |
2368 | /* */ |
- | |
2369 | /* */ |
- | |
2370 | /* Returnvalue: */ |
- | |
2371 | /********************************************************************************************************************************************/ |
- | |
2372 | - | ||
2373 | s8 *GetPath(void) |
- | |
2374 | { |
- | |
2375 | return(Partition.PathToCwd); |
- | |
2376 | } |
- | |
2377 | - | ||
2378 | /********************************************************************************************************************************************/ |
- | |
2379 | /* Function: void SetPathToRoot(void); */ |
- | |
2380 | /* */ |
- | |
2381 | /* Description: This function sets the path to the rootdirectory */ |
- | |
2382 | /* */ |
- | |
2383 | /* */ |
- | |
2384 | /* Returnvalue: */ |
- | |
2385 | /********************************************************************************************************************************************/ |
- | |
2386 | - | ||
2387 | void SetPathToRoot(void) |
- | |
2388 | { |
- | |
2389 | /* lets point to the rootdirectory */ |
- | |
2390 | strcpy(Partition.PathToCwd, "/"); |
- | |
2391 | } |
- | |
2392 | - | ||
2393 | /********************************************************************************************************************************************/ |
- | |
2394 | /* Function: void AppendDirToPath(s8* directory); */ |
- | |
2395 | /* */ |
- | |
2396 | /* Description: This function function appends the name of an directory to the Path to the CWD */ |
- | |
2397 | /* */ |
- | |
2398 | /* */ |
- | |
2399 | /* Returnvalue: */ |
- | |
2400 | /********************************************************************************************************************************************/ |
- | |
2401 | - | ||
2402 | void AppendDirToPath(s8* directory) |
- | |
2403 | { |
- | |
2404 | /* append the name of the directory to the path */ |
- | |
2405 | strcat(Partition.PathToCwd, directory); |
- | |
2406 | /* append a '/' after the directoryname */ |
- | |
2407 | strcat(Partition.PathToCwd, "/"); |
- | |
2408 | } |
- | |
2409 | - | ||
2410 | /********************************************************************************************************************************************/ |
- | |
2411 | /* Function: RemoveLastDirFromPath(void); */ |
- | |
2412 | /* */ |
- | |
2413 | /* Description: This function removes the last directory from the path to the cwd */ |
- | |
2414 | /* */ |
- | |
2415 | /* */ |
- | |
2416 | /* Returnvalue: */ |
- | |
2417 | /********************************************************************************************************************************************/ |
- | |
2418 | - | ||
2419 | void RemoveLastDirFromPath(void) |
- | |
2420 | { |
- | |
2421 | /* a pointer to the beginning of the absolute path to the cwd */ |
- | |
2422 | s8 * cptr = Partition.PathToCwd; |
- | |
2423 | /* lets find the end of the path to the cwd */ |
- | |
2424 | while(*cptr != 0 && (SD_WatchDog)) cptr++; |
- | |
2425 | /* if the path is terminated with an '/' */ |
- | |
2426 | if((*(cptr-1)) == '/') *(cptr-1)=0; |
- | |
2427 | /* now lets find the beginning of the last directorientry */ |
- | |
2428 | while((*cptr != '/' && (SD_WatchDog)) && cptr > Partition.PathToCwd) cptr--; |
- | |
2429 | /* is there one subdirectory left within the path? */ |
- | |
2430 | if(cptr > Partition.PathToCwd) |
- | |
2431 | { |
- | |
2432 | /* we delete the direntry by terminating the path with 0 */ |
- | |
2433 | *cptr = 0; |
- | |
2434 | } |
- | |
2435 | /* there is no subdirectory left within the path. Therefore we create the root instead. */ |
- | |
2436 | else |
- | |
2437 | { |
- | |
2438 | *cptr = '/'; |
- | |
2439 | *(cptr+1) = 0; |
- | |
2440 | } |
- | |
2441 | } |
- | |
2442 | - | ||
2443 | /********************************************************************************************************************************************/ |
- | |
2444 | /* Function: chdir_(s8* filepath); */ |
- | |
2445 | /* */ |
- | |
2446 | /* Description: This function changed the current working directory to the directory specified by the filepath */ |
- | |
2447 | /* by function findfirst() */ |
- | |
2448 | /* */ |
- | |
2449 | /* Returnvalue: */ |
- | |
2450 | /********************************************************************************************************************************************/ |
- | |
2451 | /* |
- | |
2452 | #define ATTR_NONE 0x00 // normal file |
- | |
2453 | #define ATTR_READONLY 0x01 // file is readonly |
- | |
2454 | #define ATTR_HIDDEN 0x02 // file is hidden |
- | |
2455 | #define ATTR_SYSTEM 0x04 // file is a system file |
- | |
2456 | #define ATTR_VOLUMELABEL 0x08 // entry is a volume label |
- | |
2457 | #define ATTR_LONG_FILENAME 0x0F // this is a long filename entry |
- | |
2458 | #define ATTR_SUBDIRECTORY 0x10 // entry is a directory name |
- | |
2459 | #define ATTR_ARCHIVE 0x20 // file is new or modified |
- | |
2460 | */ |
- | |
2461 | - | ||
2462 | u8 chdir_(s8 *path) |
- | |
2463 | { |
- | |
2464 | u8 retvalue = 0; // the value returned by this function |
- | |
2465 | u32 ultemp = 0; // temp. variable |
- | |
2466 | u8 *directory = path; // pointer to a directoryname within the path |
- | |
2467 | u8 dircount = 0; // the number of subdirectoryentries within the path |
- | |
2468 | u8 cache[64]; // a buffer containing the name of the subdirectory we are actually looking for |
- | |
2469 | Find_t fe; // The findelement needed for function findfirst to find the subdirectoryentry |
- | |
2470 | s8 tp[256]; // temporarily we remember the actual path until the operation has finished successfully |
- | |
2471 | u32 cwdt = 0; |
- | |
2472 | s8 *cptr; |
- | |
2473 | - | ||
2474 | /* bring the path into the correct syntax */ |
- | |
2475 | cptr = path; |
- | |
2476 | /* search the whole string */ |
- | |
2477 | while(*cptr != 0 && (SD_WatchDog)) |
- | |
2478 | { |
- | |
2479 | if(*cptr == '\\') *cptr = '/'; |
- | |
2480 | cptr++; |
- | |
2481 | } |
- | |
2482 | /* lets remember the actual path */ |
- | |
2483 | strcpy(tp, Partition.PathToCwd); |
- | |
2484 | cwdt = Partition.CurrentWorkingDirectory; |
- | |
2485 | /* how many subdirectories are there within the path? */ |
- | |
2486 | dircount = GetDirCount(path); |
- | |
2487 | /* if the path is absolute we begin at the rootdirectory */ |
- | |
2488 | if(path[0] == '/') |
- | |
2489 | { |
- | |
2490 | strcpy(Partition.PathToCwd, "/"); |
- | |
2491 | Partition.CurrentWorkingDirectory = Partition.FirstRootDirSector; |
- | |
2492 | /* if there is no other pathinformation we only switch to the rootdirectory. So theres nothing left todo.*/ |
- | |
2493 | if(!dircount) return(1); |
- | |
2494 | } |
- | |
2495 | /* now we parse through all the subdirectories within the path */ |
- | |
2496 | do |
- | |
2497 | { |
- | |
2498 | /* until all the subdirectories within the path have been processed */ |
- | |
2499 | if(dircount) dircount--; |
- | |
2500 | /* this is the name of the next subdirectory we are looking for */ |
- | |
2501 | directory = GetSubDirectory(directory, cache); |
- | |
2502 | /* search for the next subdirectory within the path */ |
- | |
2503 | if(findfirst_(cache, ATTR_SUBDIRECTORY, &fe)) |
- | |
2504 | { |
- | |
2505 | /* we try to change into the directory "..". Now we have to delete the last direntry from the path */ |
- | |
2506 | if(strcmp(cache,"..") == 0) RemoveLastDirFromPath(); |
- | |
2507 | /* we try to change into the actual directory so there's nothing todo */ |
- | |
2508 | else if(cache[0] == '.') return(1); |
- | |
2509 | /* otherwise we append the name of the directory we are changing in to the path */ |
- | |
2510 | else AppendDirToPath(cache); |
- | |
2511 | /* The startcluster within an directoryentry specifies the position within the fat where the file or directory starts */ |
- | |
2512 | ultemp = (u32) fe.fp.FirstSectorOfFirstCluster; |
- | |
2513 | /* the first 2 entries are reserved for '.' and '..' */ |
- | |
2514 | ultemp -= 2; |
- | |
2515 | /* now we have to transform the position within the fat into the corrosponding sectoraddress relative to the beginning of the datasection of the active partition*/ |
- | |
2516 | ultemp *= Partition.SectorsPerCluster; |
- | |
2517 | /* at least we make the sectoraddress absolute by adding the relative address to the beginning of the datasection of the active partition */ |
- | |
2518 | ultemp += Partition.FirstDataSector; |
- | |
2519 | /* the cwd now points to the specified directory */ |
- | |
2520 | Partition.CurrentWorkingDirectory = ultemp; |
- | |
2521 | /* we found the folder specified by the foldername */ |
- | |
2522 | retvalue = 1; |
- | |
2523 | } |
- | |
2524 | } |
- | |
2525 | /* do this until all subdirectories have been found or a subdirectory is missing */ |
- | |
2526 | while(dircount && retvalue && (SD_WatchDog)); |
- | |
2527 | - | ||
2528 | /* if we could not change to the specified directory we restore the actual path */ |
- | |
2529 | if(!retvalue) |
- | |
2530 | { |
- | |
2531 | Partition.CurrentWorkingDirectory = cwdt; |
- | |
2532 | strcpy(Partition.PathToCwd, tp); |
- | |
2533 | } |
- | |
2534 | return(retvalue); |
- |