|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 #include "fsnode.h" |
|
18 #include "fatdefines.h" |
|
19 #include "utf16string.h" |
|
20 #include <stdio.h> |
|
21 #include <iostream> |
|
22 #include <iomanip> |
|
23 #include <stdio.h> |
|
24 #include <stdlib.h> |
|
25 |
|
26 #include <ctype.h> |
|
27 |
|
28 |
|
29 #ifdef __LINUX__ |
|
30 #include <dirent.h> |
|
31 #include <sys/stat.h> |
|
32 #include <unistd.h> |
|
33 #define SPLIT_CHAR '/' |
|
34 #else |
|
35 #include <io.h> |
|
36 #include <direct.h> //TODO: check under MinGW4 + stlport 5.2 |
|
37 #include <conio.h> |
|
38 #define SPLIT_CHAR '\\' |
|
39 #endif |
|
40 |
|
41 using namespace std; |
|
42 |
|
43 const TUint KBytesPerEntry = 13 ; |
|
44 // |
|
45 TFSNode::TFSNode(TFSNode* aParent, const char* aFileName, TUint8 aAttrs, const char* aPCSideName) : |
|
46 iParent(aParent),iFirstChild(0),iSibling(0),iAttrs(aAttrs), iPCSideName(0), iWideName(0){ |
|
47 |
|
48 // According to the FAT specification, short name should be inited with empty string (' ' string) |
|
49 memset(iShortName,0x20,11); |
|
50 iShortName[11] = 0 ; |
|
51 if(aFileName) { |
|
52 iFileName = strdup(aFileName); |
|
53 GenerateBasicName() ; |
|
54 } |
|
55 if(aPCSideName) { |
|
56 iPCSideName = strdup(aPCSideName); |
|
57 } |
|
58 iFATEntry = 0; |
|
59 iCrtTimeTenth = 0; |
|
60 iCrtTime.iImageTime = 0 ; |
|
61 iCrtDate.iImageDate = 0 ; |
|
62 iLstAccDate.iImageDate = 0 ; |
|
63 iWrtTime.iImageTime = 0 ; |
|
64 iWrtDate.iImageDate = 0 ; |
|
65 iFileSize = 0; |
|
66 if(!iParent) return ; |
|
67 |
|
68 if(!iParent->iFirstChild) |
|
69 iParent->iFirstChild = this ; |
|
70 else { |
|
71 TFSNode* sibling = iParent->iFirstChild; |
|
72 while(sibling->iSibling) |
|
73 sibling = sibling->iSibling ; |
|
74 sibling->iSibling = this ; |
|
75 } |
|
76 |
|
77 } |
|
78 TFSNode::~TFSNode(){ |
|
79 if(iFirstChild) |
|
80 delete iFirstChild ; |
|
81 if(iSibling) |
|
82 delete iSibling ; |
|
83 if(iFileName) |
|
84 free(iFileName) ; |
|
85 if(iWideName) |
|
86 delete iWideName; |
|
87 } |
|
88 TFSNode* TFSNode::CreateFromFolder(const char* aPath,TFSNode* aParent) { |
|
89 static char fileName[2048]; |
|
90 int len = strlen(aPath); |
|
91 #ifdef __LINUX__ |
|
92 DIR* dir = opendir(aPath); |
|
93 if(dir == NULL) { |
|
94 cout << aPath << " does not contain any subfolder/file.\n"; |
|
95 return aParent; |
|
96 } |
|
97 if(!aParent) |
|
98 aParent = new TFSNode(NULL,"/",ATTR_VOLUME_ID); |
|
99 dirent* entry; |
|
100 struct stat statbuf ; |
|
101 while ((entry = readdir(dir)) != NULL) { |
|
102 if(entry->d_name[0] == '.') continue ; |
|
103 memcpy(fileName,aPath,len); |
|
104 fileName[len] = SPLIT_CHAR; |
|
105 strcpy(&fileName[len+1],entry->d_name); |
|
106 stat(fileName , &statbuf); |
|
107 TFSNode* pNewItem = new TFSNode(aParent,fileName,S_ISDIR(statbuf.st_mode) ? ATTR_DIRECTORY : 0); |
|
108 pNewItem->Init(statbuf.st_ctime,statbuf.st_atime,statbuf.st_mtime,statbuf.st_size); |
|
109 if(S_ISDIR(statbuf.st_mode)){ |
|
110 CreateFromFolder(fileName,pNewItem); |
|
111 } |
|
112 } |
|
113 closedir(dir); |
|
114 #else |
|
115 struct _finddata_t data ; |
|
116 memset(&data, 0, sizeof(data)); |
|
117 char* pattern = new char[len + 4] ; |
|
118 memcpy(pattern,aPath,len); |
|
119 pattern[len] = SPLIT_CHAR; |
|
120 pattern[len+1] = '*'; |
|
121 pattern[len+2] = 0; |
|
122 |
|
123 intptr_t hFind = _findfirst(pattern,&data); |
|
124 delete []pattern ; |
|
125 |
|
126 if(hFind == (intptr_t)-1 ) { |
|
127 cout << aPath << " does not contain any subfolder/file.\n"; |
|
128 return aParent; |
|
129 } |
|
130 if(!aParent) |
|
131 aParent = new TFSNode(NULL,"/",ATTR_VOLUME_ID); |
|
132 do { |
|
133 if(data.name[0] == '.') |
|
134 continue ; |
|
135 memcpy(fileName,aPath,len); |
|
136 fileName[len] = SPLIT_CHAR; |
|
137 strcpy(&fileName[len+1],data.name); |
|
138 TUint8 attr = 0; |
|
139 if(data.attrib & _A_SUBDIR) |
|
140 attr |= ATTR_DIRECTORY; |
|
141 if(data.attrib & _A_RDONLY) |
|
142 attr |= ATTR_READ_ONLY ; |
|
143 if(data.attrib & _A_HIDDEN) |
|
144 attr |= ATTR_HIDDEN ; |
|
145 if(data.attrib & _A_SYSTEM) |
|
146 attr |= ATTR_SYSTEM ; |
|
147 if(data.attrib & _A_ARCH) |
|
148 attr |= ATTR_ARCHIVE; |
|
149 TFSNode* pNewItem = new TFSNode(aParent,fileName,attr); |
|
150 pNewItem->Init(data.time_create,data.time_access,data.time_write,data.size); |
|
151 if(data.attrib & _A_SUBDIR){ |
|
152 CreateFromFolder(fileName,pNewItem); |
|
153 } |
|
154 |
|
155 } while(-1 != _findnext(hFind, &data)); |
|
156 _findclose(hFind); |
|
157 #endif |
|
158 |
|
159 return aParent; |
|
160 } |
|
161 |
|
162 |
|
163 |
|
164 static const char* lbasename(const char* aFullName) { |
|
165 const char* retval = aFullName ; |
|
166 while(*aFullName) { |
|
167 if('\\' == *aFullName || '/' == *aFullName ) |
|
168 retval = ++aFullName ; |
|
169 else |
|
170 aFullName ++ ; |
|
171 } |
|
172 return retval ; |
|
173 } |
|
174 /** GenerateBasicName : Generate the short name according to long name |
|
175 * |
|
176 * algorithm : |
|
177 * |
|
178 * 1. The UNICODE name passed to the file system is converted to upper case. |
|
179 * 2. The upper cased UNICODE name is converted to OEM. |
|
180 * if (the uppercased UNICODE glyph does not exist as an OEM glyph in the OEM code page) |
|
181 * or (the OEM glyph is invalid in an 8.3 name) |
|
182 * { |
|
183 * Replace the glyph to an OEM '_' (underscore) character. |
|
184 * Set a "lossy conversion" flag. |
|
185 * } |
|
186 * 3. Strip all leading and embedded spaces from the long name. |
|
187 * 4. Strip all leading periods from the long name. |
|
188 * 5. While (not at end of the long name) |
|
189 * and (char is not a period) |
|
190 * and (total chars copied < 8) |
|
191 * { |
|
192 * Copy characters into primary portion of the basis name |
|
193 * } |
|
194 * 6. Insert a dot at the end of the primary components of the basis-name |
|
195 * if the basis name has an extension after the last period in the name. |
|
196 * |
|
197 * 7. Scan for the last embedded period in the long name. |
|
198 * If (the last embedded period was found) |
|
199 * { |
|
200 * While (not at end of the long name) and (total chars copied < 3) |
|
201 * { |
|
202 * Copy characters into extension portion of the basis name |
|
203 * } |
|
204 * } |
|
205 * |
|
206 */ |
|
207 void TFSNode::GenerateBasicName() { |
|
208 const char* filename = lbasename(iFileName); |
|
209 TUint length = strlen(filename); |
|
210 if(0 == length) |
|
211 return ; |
|
212 if(0 == strcmp(filename,".")){ |
|
213 iShortName[0] = '.' ; |
|
214 return ; |
|
215 } |
|
216 if(0 == strcmp(filename,"..")){ |
|
217 iShortName[0] = '.' ; |
|
218 iShortName[1] = '.' ; |
|
219 return ; |
|
220 } |
|
221 #ifdef _DEBUG |
|
222 cout << "GenericBasicName: \"" << filename ; |
|
223 #endif |
|
224 iWideName = new UTF16String(filename,length); // The unicode string |
|
225 char base[10]; |
|
226 const char* ext = filename + length; |
|
227 |
|
228 //Strip all leading periods and spaces from the long name. |
|
229 while(*filename == '.' || *filename == ' ' || *filename == '\t') { |
|
230 filename ++ ; |
|
231 length -- ; |
|
232 } |
|
233 //find the extension |
|
234 while(ext > filename && *ext != '.') |
|
235 ext -- ; |
|
236 if(ext == filename){ |
|
237 ext = "" ; |
|
238 } |
|
239 else { |
|
240 length = ext - filename; |
|
241 ext ++ ; |
|
242 } |
|
243 bool lossyConv = false ; |
|
244 TUint bl = 0; |
|
245 for(TUint i = 0 ; i < length ; i++) { |
|
246 if(filename[i] >= 'a' && filename[i] <= 'z') |
|
247 base[bl++] = filename[i] + 'A' - 'a'; |
|
248 else if(filename[i] >= 'A' && filename[i] <= 'Z') |
|
249 base[bl++] = filename[i]; |
|
250 else if(filename[i] == '$' || filename[i] == '%' || |
|
251 filename[i] == '-' || filename[i] == '_' || filename[i] == '@' || |
|
252 filename[i] == '~' || filename[i] == '`' || filename[i] == '!' || |
|
253 filename[i] == '(' || filename[i] == ')' || filename[i] == '{' || |
|
254 filename[i] == '}' || filename[i] == '^' || filename[i] == '#' || |
|
255 filename[i] == '&' ||filename[i] == '\'') |
|
256 base[bl++] = filename[i]; |
|
257 else if(filename[i] != ' ' && filename[i] != '.'){ |
|
258 base[bl++] = '_'; |
|
259 lossyConv = true ; |
|
260 } |
|
261 if(bl > 8){ |
|
262 bl -- ; |
|
263 lossyConv = true ; |
|
264 break ; |
|
265 } |
|
266 } |
|
267 if(lossyConv){ |
|
268 if(bl > 6) bl = 6 ; |
|
269 iShortName[bl] = '~'; |
|
270 iShortName[bl+1] = '1'; |
|
271 } |
|
272 memcpy(iShortName,base,bl); |
|
273 |
|
274 //Copy the extension part. |
|
275 TUint ei = 8; |
|
276 for(TUint e = 0; ei < 11 && ext[e] != 0 ; e++){ |
|
277 if(ext[e] >= 'a' && ext[e] <= 'z') |
|
278 iShortName[ei++] = ext[e] + 'A' - 'a'; |
|
279 else if(ext[e] >= 'A' && ext[e] <= 'Z') |
|
280 iShortName[ei++] = ext[e] ; |
|
281 else if(ext[e] == '$' || ext[e] == '%' || ext[e] == '-' || ext[e] == '_' || |
|
282 ext[e] == '@' || ext[e] == '~' || ext[e] == '`' || ext[e] == '!' || |
|
283 ext[e] == '(' || ext[e] == ')' || ext[e] == '{' || ext[e] == '}' || |
|
284 ext[e] == '^' || ext[e] == '#' || ext[e] == '&' ||ext[e] == '\'') |
|
285 iShortName[ei++] = ext[e] ; |
|
286 } |
|
287 |
|
288 if(iParent) |
|
289 iParent->MakeUniqueShortName(iShortName,bl); |
|
290 #ifdef _DEBUG |
|
291 cout << "\" => \"" << iShortName << "\"\n"; |
|
292 #endif |
|
293 } |
|
294 |
|
295 #ifdef _DEBUG |
|
296 void TFSNode::PrintTree(int nTab) { |
|
297 for( int i = 0 ; i < nTab ; i++ ) |
|
298 cout << " " ; |
|
299 cout << (iFileName ? iFileName : "") << " [" << hex << setw(2) << setfill('0') << (unsigned short)iAttrs << "] \n" ; |
|
300 if(iFirstChild) |
|
301 iFirstChild->PrintTree(nTab + 2); |
|
302 if(iSibling) |
|
303 iSibling->PrintTree(nTab); |
|
304 } |
|
305 #endif |
|
306 bool TFSNode::IsDirectory() const { |
|
307 return 0 != (iAttrs & ATTR_DIRECTORY); |
|
308 } |
|
309 int TFSNode::GetWideNameLength() const { |
|
310 if(!iWideName) |
|
311 return 0 ; |
|
312 return iWideName->length() ; |
|
313 } |
|
314 TUint TFSNode::GetSize() const { |
|
315 |
|
316 if( 0 == (iAttrs & ATTR_DIRECTORY)) |
|
317 return iFileSize ; |
|
318 TUint retVal = sizeof(TShortDirEntry) ; // the tailed entry |
|
319 if(iParent) |
|
320 retVal += sizeof(TShortDirEntry) * 2 ; |
|
321 TFSNode* child = iFirstChild ; |
|
322 while(child) { |
|
323 TUint longNameEntries = (child->GetWideNameLength() + KBytesPerEntry) / KBytesPerEntry ; |
|
324 retVal += longNameEntries * sizeof(TLongDirEntry) ; |
|
325 retVal += sizeof(TShortDirEntry); |
|
326 child = child->iSibling ; |
|
327 } |
|
328 return retVal ; |
|
329 } |
|
330 |
|
331 void TFSNode::Init(time_t aCreateTime, time_t aAccessTime, time_t aWriteTime, TUint aSize ) { |
|
332 |
|
333 struct tm* temp = localtime(&aCreateTime); |
|
334 iCrtDate.iCurrentDate.Day = temp->tm_mday; |
|
335 iCrtDate.iCurrentDate.Month = temp->tm_mon+1; //As per FAT spec |
|
336 iCrtDate.iCurrentDate.Year = temp->tm_year - 80;//As per FAT spec |
|
337 iCrtTime.iCurrentTime.Hour = temp->tm_hour; |
|
338 iCrtTime.iCurrentTime.Minute = temp->tm_min; |
|
339 iCrtTime.iCurrentTime.Seconds = temp->tm_sec / 2;//As per FAT spec |
|
340 iCrtTimeTenth = 0; |
|
341 |
|
342 temp = localtime(&aAccessTime); |
|
343 iLstAccDate.iCurrentDate.Day = temp->tm_mday; |
|
344 iLstAccDate.iCurrentDate.Month = temp->tm_mon+1; //As per FAT spec |
|
345 iLstAccDate.iCurrentDate.Year = temp->tm_year - 80;//As per FAT spec |
|
346 |
|
347 temp = localtime(&aWriteTime); |
|
348 iWrtDate.iCurrentDate.Day = temp->tm_mday; |
|
349 iWrtDate.iCurrentDate.Month = temp->tm_mon+1; //As per FAT spec |
|
350 iWrtDate.iCurrentDate.Year = temp->tm_year - 80;//As per FAT spec |
|
351 iWrtTime.iCurrentTime.Hour = temp->tm_hour; |
|
352 iWrtTime.iCurrentTime.Minute = temp->tm_min; |
|
353 iWrtTime.iCurrentTime.Seconds = temp->tm_sec / 2;//As per FAT spec |
|
354 |
|
355 iFileSize = aSize ; |
|
356 } |
|
357 /** WriteDirEntries : Write FAT information for this node to a cluster buffer |
|
358 * aStartIndex : [in],the beginning index of the outputed cluster |
|
359 * aClusterData : [in,out] the cluster buffer |
|
360 * |
|
361 * notice, aClusterData is only required if node is a directory node. |
|
362 * for a file node, no data will be written out. |
|
363 * in this case, only corresponding cluster index information is updated. |
|
364 */ |
|
365 void TFSNode::WriteDirEntries(TUint aStartIndex,TUint8* aClusterData){ |
|
366 if(iFATEntry){ |
|
367 *((TUint16*)iFATEntry->DIR_FstClusHI) = (aStartIndex >> 16) ; |
|
368 *((TUint16*)iFATEntry->DIR_FstClusLO) = (aStartIndex & 0xFFFF) ; |
|
369 } |
|
370 |
|
371 if(iAttrs & ATTR_DIRECTORY) { // Directory , write dir entries ; |
|
372 TShortDirEntry* entry = reinterpret_cast<TShortDirEntry*>(aClusterData); |
|
373 if(iParent != NULL) { |
|
374 //Make |
|
375 GetShortEntry(entry); |
|
376 //TODO: Add comments to avoid mistaken deleting. |
|
377 memcpy(entry->DIR_Name,". ",sizeof(entry->DIR_Name)); |
|
378 entry ++ ; |
|
379 iParent->GetShortEntry(entry); |
|
380 memcpy(entry->DIR_Name,".. ",sizeof(entry->DIR_Name)); |
|
381 entry ++ ; |
|
382 } |
|
383 TFSNode* child = iFirstChild ; |
|
384 while(child){ |
|
385 int items = child->GetLongEntries(reinterpret_cast<TLongDirEntry*>(entry)); |
|
386 entry += items ; |
|
387 child->GetShortEntry(entry); |
|
388 child->iFATEntry = entry ; |
|
389 entry ++ ; |
|
390 child = child->iSibling ; |
|
391 |
|
392 } |
|
393 |
|
394 } |
|
395 } |
|
396 /** GetShortEntry : Make a short directory entry (FAT16/32 conception) |
|
397 * aEntry : the entry buffer |
|
398 */ |
|
399 void TFSNode::GetShortEntry(TShortDirEntry* aEntry) { |
|
400 if(!aEntry) return ; |
|
401 if(iFATEntry){ |
|
402 if(iFATEntry != aEntry) |
|
403 memcpy(aEntry,iFATEntry,sizeof(TShortDirEntry)); |
|
404 return ; |
|
405 } |
|
406 memcpy(aEntry->DIR_Name,iShortName,sizeof(aEntry->DIR_Name)); |
|
407 aEntry->DIR_Attr = iAttrs; |
|
408 aEntry->DIR_NTRes = 0 ; |
|
409 aEntry->DIR_CrtTimeTenth = 0 ; |
|
410 memcpy(aEntry->DIR_CrtTime,&iCrtTime,sizeof(aEntry->DIR_CrtTime)); |
|
411 memcpy(aEntry->DIR_CrtDate,&iCrtDate,sizeof(aEntry->DIR_CrtDate)); |
|
412 memcpy(aEntry->DIR_LstAccDate,&iLstAccDate,sizeof(aEntry->DIR_LstAccDate)); |
|
413 memset(aEntry->DIR_FstClusHI,0,sizeof(aEntry->DIR_FstClusHI)); |
|
414 memcpy(aEntry->DIR_WrtTime,&iWrtTime,sizeof(aEntry->DIR_WrtTime)); |
|
415 memcpy(aEntry->DIR_WrtDate,&iWrtDate,sizeof(aEntry->DIR_WrtDate)); |
|
416 memset(aEntry->DIR_FstClusLO,0,sizeof(aEntry->DIR_FstClusLO)); |
|
417 memcpy(aEntry->DIR_FileSize,&iFileSize,sizeof(aEntry->DIR_FileSize)); |
|
418 } |
|
419 TUint8 FATChkSum(const char* pFcbName) { |
|
420 short fcbNameLen ; |
|
421 TUint8 sum = 0 ; |
|
422 for(fcbNameLen = 11 ; fcbNameLen != 0 ; fcbNameLen --) { |
|
423 sum = ((sum & 1) ? 0x80 : 0 ) + (sum >> 1 ) + *pFcbName++ ; |
|
424 } |
|
425 return sum ; |
|
426 } |
|
427 /** GetLongEntries : Make a series of long directory entries (FAT16/32 conception) |
|
428 * aEntries : the start addr of the long directory entries buffer |
|
429 * |
|
430 * return value : actual entris count. |
|
431 */ |
|
432 int TFSNode::GetLongEntries(TLongDirEntry* aEntries) { |
|
433 |
|
434 if(!aEntries) return 0; |
|
435 int packs = (GetWideNameLength() + KBytesPerEntry) / KBytesPerEntry ; |
|
436 |
|
437 TUint buflen = packs * KBytesPerEntry; |
|
438 TUint16* buffer = new(std::nothrow) TUint16[buflen]; |
|
439 if(!buffer) |
|
440 return 0 ; |
|
441 memset(buffer,0xff,(buflen << 1)); |
|
442 if(iWideName) { |
|
443 memcpy(buffer,iWideName->c_str(),iWideName->bytes()); |
|
444 buffer[iWideName->length()] = 0; |
|
445 } |
|
446 TUint8 chkSum = FATChkSum(iShortName);; |
|
447 |
|
448 TUint16* ptr = buffer ; |
|
449 TLongDirEntry* entry = aEntries +(packs - 1); |
|
450 for(int i = 1 ; i <= packs ; i++, entry--) { |
|
451 entry->LDIR_Ord = i ; |
|
452 entry->LDIR_Chksum = chkSum ; |
|
453 entry->LDIR_Attr = (TUint8)ATTR_LONG_NAME; |
|
454 *((TUint16*)(entry->LDIR_FstClusLO)) = 0; |
|
455 entry->LDIR_Type = 0; |
|
456 memcpy(entry->LDIR_Name1,ptr,10); |
|
457 memcpy(entry->LDIR_Name2,&ptr[5],12); |
|
458 memcpy(entry->LDIR_Name3,&ptr[11],4); |
|
459 ptr += 13; |
|
460 } |
|
461 aEntries->LDIR_Ord |= 0x40 ; |
|
462 |
|
463 delete []buffer ; |
|
464 return packs ; |
|
465 } |
|
466 /** Make a unique name for a new child which has not been added. |
|
467 * to avoid same short names under a directory |
|
468 * rShortName : [in,out] , The new short name to be checked and changed. |
|
469 * baseNameLength: [in], the length of the base part of the short name |
|
470 * not including the "~n" |
|
471 * for example, |
|
472 * "ABC.LOG" => baseNameLength == 3 ("ABC") |
|
473 * "AB~1.TXT" => baseNameLength == 2 ("AB") |
|
474 * |
|
475 * |
|
476 *The Numeric-Tail Generation Algorithm |
|
477 |
|
478 * If (a "lossy conversion" was not flagged) |
|
479 * and (the long name fits within the 8.3 naming conventions) |
|
480 * and (the basis-name does not collide with any existing short name) |
|
481 * { |
|
482 * The short name is only the basis-name without the numeric tail. |
|
483 * } |
|
484 * else { |
|
485 * Insert a numeric-tail "~n" to the end of the primary name such that the value of |
|
486 * the "~n" is chosen so that the name thus formed does not collide with |
|
487 * any existing short name and that the primary name does not exceed eight |
|
488 * characters in length. |
|
489 * } |
|
490 * The "~n" string can range from "~1" to "~999999". |
|
491 * |
|
492 */ |
|
493 |
|
494 void TFSNode::MakeUniqueShortName(char rShortName[12],TUint baseNameLength) const { |
|
495 bool dup ; |
|
496 char nstring[10]; |
|
497 int n = 0 ; |
|
498 do { |
|
499 TFSNode* child = iFirstChild ; |
|
500 dup = false ; |
|
501 while(child){ |
|
502 if(0 == memcmp(rShortName,child->iShortName,11)) { |
|
503 dup = true ; |
|
504 break ; |
|
505 } |
|
506 child = child->iSibling ; |
|
507 } |
|
508 if(dup){ //duplex , increase the index , make a new name |
|
509 int nlen = sprintf(nstring,"~%u",++n); |
|
510 while((baseNameLength + nlen > 8) && baseNameLength > 1) |
|
511 baseNameLength -- ; |
|
512 memcpy(&rShortName[baseNameLength],nstring,nlen); |
|
513 |
|
514 } |
|
515 }while(dup) ; |
|
516 |
|
517 } |
|
518 |