1 #include "normFile.h"
2
3 #include <string.h> // for strerror()
4 #include <stdio.h> // for rename()
5 #ifdef WIN32
6 #ifndef _WIN32_WCE
7 #include <errno.h>
8 #include <direct.h>
9 #include <share.h>
10 #include <io.h>
11 #endif // !_WIN32_WCE
12 #else
13 #include <unistd.h>
14 // Most don't have the dirfd() function
15 #ifndef HAVE_DIRFD
dirfd(DIR * dir)16 static inline int dirfd(DIR *dir) {return (dir->dd_fd);}
17 #endif // HAVE_DIRFD
18 #endif // if/else WIN32
19
20 #ifndef _WIN32_WCE
21 #include <fcntl.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #endif // !_WIN32_WCE
25
NormFile()26 NormFile::NormFile()
27 #ifdef _WIN32_WCE
28 : file_ptr(NULL)
29 #else
30 : fd(-1)
31 #endif // if/else _WIN32_WCE
32 {
33 }
34
~NormFile()35 NormFile::~NormFile()
36 {
37 if (IsOpen()) Close();
38 } // end NormFile::~NormFile()
39
40
41 // This should be called with a full path only!
Open(const char * thePath,int theFlags)42 bool NormFile::Open(const char* thePath, int theFlags)
43 {
44 ASSERT(!IsOpen());
45 if (theFlags & O_CREAT)
46 {
47 // Create sub-directories as needed.
48 char tempPath[PATH_MAX];
49 strncpy(tempPath, thePath, PATH_MAX);
50 char* ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
51 if (NULL != ptr)
52 {
53 *ptr = '\0';
54 ptr = NULL;
55 while (!NormFile::Exists(tempPath))
56 {
57 char* ptr2 = ptr;
58 ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
59 if (ptr2) *ptr2 = PROTO_PATH_DELIMITER;
60 if (ptr)
61 {
62 *ptr = '\0';
63 }
64 else
65 {
66 ptr = tempPath;
67 break;
68 }
69 }
70 }
71 if (ptr && ('\0' == *ptr)) *ptr++ = PROTO_PATH_DELIMITER;
72 while (ptr)
73 {
74 ptr = strchr(ptr, PROTO_PATH_DELIMITER);
75 if (ptr) *ptr = '\0';
76 #ifdef WIN32
77 #ifdef _WIN32_WCE
78 #ifdef _UNICODE
79 wchar_t wideBuffer[MAX_PATH];
80 size_t pathLen = strlen(tempPath) + 1;
81 if (pathLen > MAX_PATH) pathLen = MAX_PATH;
82 mbstowcs(wideBuffer, tempPath, pathLen);
83 if (!CreateDirectory(wideBuffer, NULL))
84 #else
85 if (!CreateDirectory(tempPath, NULL))
86 #endif // if/else _UNICODE
87 #else
88 if (_mkdir(tempPath))
89 #endif // if/else _WIN32_WCE
90 #else
91 if (mkdir(tempPath, 0755))
92 #endif // if/else WIN32/UNIX
93 {
94 PLOG(PL_FATAL, "NormFile::Open() mkdir(%s) error: %s\n",
95 tempPath, GetErrorString());
96 return false;
97 }
98 if (ptr) *ptr++ = PROTO_PATH_DELIMITER;
99 }
100 }
101 #ifdef WIN32
102 // Make sure we're in binary mode (important for WIN32)
103 theFlags |= O_BINARY;
104 #ifdef _WIN32_WCE
105 if (theFlags & O_RDONLY)
106 file_ptr = fopen(thePath, "rb");
107 else
108 file_ptr = fopen(thePath, "w+b");
109 if (NULL != file_ptr)
110 #else
111 // Allow sharing of read-only files but not of files being written
112 if (theFlags & O_RDONLY)
113 fd = _sopen(thePath, theFlags, _SH_DENYNO);
114 else
115 fd = _open(thePath, theFlags, 0640);
116 if(fd >= 0)
117 #endif // if/else _WIN32_WCE
118 {
119 offset = 0;
120 flags = theFlags;
121 return true; // no error
122 }
123 else
124 {
125 PLOG(PL_FATAL, "Error opening file \"%s\": %s\n", thePath, GetErrorString());
126 flags = 0;
127 return false;
128 }
129 #else
130 if((fd = open(thePath, theFlags, 0640)) >= 0)
131 {
132 offset = 0;
133 return true; // no error
134 }
135 else
136 {
137 PLOG(PL_FATAL, "norm: Error opening file \"%s\": %s\n",
138 thePath, GetErrorString());
139 return false;
140 }
141 #endif // if/else WIN32
142 } // end NormFile::Open()
143
Close()144 void NormFile::Close()
145 {
146 if (IsOpen())
147 {
148 #ifdef WIN32
149 #ifdef _WIN32_WCE
150 fclose(file_ptr);
151 file_ptr = NULL;
152 #else
153 _close(fd);
154 fd = -1;
155 #endif // if/else _WIN32_WCE
156 #else
157 close(fd);
158 fd = -1;
159 #endif // if/else WIN32
160 }
161 } // end NormFile::Close()
162
163
164 // Routines to try to get an exclusive lock on a file
Lock()165 bool NormFile::Lock()
166 {
167 #ifndef WIN32 // WIN32 files are automatically locked
168 fchmod(fd, 0640 | S_ISGID);
169 #ifdef HAVE_FLOCK
170 if (flock(fd, LOCK_EX | LOCK_NB))
171 return false;
172 else
173 #else
174 #ifdef HAVE_LOCKF
175 if (0 != lockf(fd, F_LOCK, 0)) // assume lockf if not flock
176 return false;
177 else
178 #endif // HAVE_LOCKF
179 #endif // if/else HAVE_FLOCK
180 #endif // !WIN32
181 return true;
182 } // end NormFile::Lock()
183
Unlock()184 void NormFile::Unlock()
185 {
186 #ifndef WIN32
187 #ifdef HAVE_FLOCK
188 flock(fd, LOCK_UN);
189 #else
190 #ifdef HAVE_LOCKF
191 int ret = lockf(fd, F_ULOCK, 0);
192 ret = 0; // done to avoid compiler warning (should optimize out)
193 #endif // HAVE_LOCKF
194 #endif // if/elseHAVE_FLOCK
195 fchmod(fd, 0640);
196 #endif // !WIN32
197 } // end NormFile::UnLock()
198
Rename(const char * oldName,const char * newName)199 bool NormFile::Rename(const char* oldName, const char* newName)
200 {
201 if (!strcmp(oldName, newName)) return true; // no change required
202 // Make sure the new file name isn't an existing "busy" file
203 // (This also builds sub-directories as needed)
204 if (NormFile::IsLocked(newName))
205 {
206 PLOG(PL_FATAL, "NormFile::Rename() error: file is locked\n");
207 return false;
208 }
209 #ifdef WIN32
210 // In Win32, the new file can't already exist
211 if (NormFile::Exists(newName))
212 {
213 #ifdef _WIN32_WCE
214 #ifdef _UNICODE
215 wchar_t wideBuffer[MAX_PATH];
216 size_t pathLen = strlen(newName) + 1;
217 if (pathLen > MAX_PATH) pathLen = MAX_PATH;
218 mbstowcs(wideBuffer, newName, pathLen);
219 if (0 == DeleteFile(wideBuffer))
220 #else
221 if (0 == DeleteFile(newName))
222 #endif // if/else _UNICODE
223 {
224 PLOG(PL_FATAL, "NormFile::Rename() DeleteFile() error: %s\n", GetErrorString());
225 return false;
226 }
227 #else
228 if (0 != _unlink(newName))
229 {
230 PLOG(PL_FATAL, "NormFile::Rename() _unlink() error: %s\n", GetErrorString());
231 return false;
232 }
233 #endif // if/else _WIN32_WCE
234 }
235 // In Win32, the old file can't be open
236 int oldFlags = 0;
237 if (IsOpen())
238 {
239 oldFlags = flags;
240 oldFlags &= ~(O_CREAT | O_TRUNC); // unset these
241 Close();
242 }
243 #endif // WIN32
244 // Create sub-directories as needed.
245 char tempPath[PATH_MAX];
246 strncpy(tempPath, newName, PATH_MAX);
247 char* ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
248 if (ptr) *ptr = '\0';
249 ptr = NULL;
250 while (!NormFile::Exists(tempPath))
251 {
252 char* ptr2 = ptr;
253 ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
254 if (ptr2) *ptr2 = PROTO_PATH_DELIMITER;
255 if (ptr)
256 {
257 *ptr = '\0';
258 }
259 else
260 {
261 ptr = tempPath;
262 break;
263 }
264 }
265 if (ptr && ('\0' == *ptr)) *ptr++ = PROTO_PATH_DELIMITER;
266 while (ptr)
267 {
268 ptr = strchr(ptr, PROTO_PATH_DELIMITER);
269 if (ptr) *ptr = '\0';
270 #ifdef WIN32
271 #ifdef _WIN32_WCE
272 #ifdef _UNICODE
273 wchar_t wideBuffer[MAX_PATH];
274 size_t pathLen = strlen(tempPath) + 1;
275 if (pathLen > MAX_PATH) pathLen = MAX_PATH;
276 mbstowcs(wideBuffer, tempPath, pathLen);
277 if (!CreateDirectory(wideBuffer, NULL))
278 #else
279 if (!CreateDirectory(tempPath, NULL))
280 #endif // if/else _UNICODE
281 #else
282 if (0 != _mkdir(tempPath))
283 #endif // if/else _WIN32_WCE
284 #else
285 if (mkdir(tempPath, 0755))
286 #endif // if/else WIN32/UNIX
287 {
288 PLOG(PL_FATAL, "NormFile::Rename() mkdir(%s) error: %s\n",
289 tempPath, GetErrorString());
290 return false;
291 }
292 if (ptr) *ptr++ = PROTO_PATH_DELIMITER;
293 }
294 #ifdef _WIN32_WCE
295 #ifdef _UNICODE
296 wchar_t wideOldName[MAX_PATH];
297 wchar_t wideNewName[MAX_PATH];
298 mbstowcs(wideOldName, oldName, MAX_PATH);
299 mbstowcs(wideNewName, newName, MAX_PATH);
300 if (!MoveFile(wideOldName, wideNewName))
301 #else
302 if (!MoveFile(oldName, newName))
303 #endif // if/else _UNICODE
304 {
305 PLOG(PL_ERROR, "NormFile::Rename() MoveFile() error: %s\n", GetErrorString());
306 #else
307 if (rename(oldName, newName))
308 {
309 PLOG(PL_ERROR, "NormFile::Rename() rename() error: %s\n", GetErrorString());
310 #endif // if/else _WIN32_WCE
311 #ifdef WIN32
312 if (oldFlags)
313 {
314 if (Open(oldName, oldFlags))
315 offset = 0;
316 else
317 PLOG(PL_ERROR, "NormFile::Rename() error re-opening file w/ old name\n");
318 }
319 #endif // WIN32
320 return false;
321 }
322 else
323 {
324 #ifdef WIN32
325 // (TBD) Is the file offset OK doing this???
326 if (oldFlags)
327 {
328 if (Open(newName, oldFlags))
329 offset = 0;
330 else
331 PLOG(PL_ERROR, "NormFile::Rename() error opening file w/ new name\n");
332 }
333 #endif // WIN32
334 return true;
335 }
336 } // end NormFile::Rename()
337
338 size_t NormFile::Read(char* buffer, size_t len)
339 {
340 ASSERT(IsOpen());
341
342 size_t got = 0;
343 while (got < len)
344 {
345 #ifdef WIN32
346 #ifdef _WIN32_WCE
347 size_t result = fread(buffer+got, 1, len-got, file_ptr);
348 #else
349 size_t result = _read(fd, buffer+got, (unsigned int)(len-got));
350 #endif // if/else _WIN32_WCE
351 #else
352 ssize_t result = read(fd, buffer+got, len-got);
353 #endif // if/else WIN32
354 if (result <= 0)
355 {
356 #ifndef _WIN32_WCE
357 if (EINTR != errno)
358 #endif // !_WIN32_WCE
359 {
360 PLOG(PL_FATAL, "NormFile::Read() read(%d) result:%d error:%s (offset:%d)\n", len, result, GetErrorString(), offset);
361 return 0;
362 }
363 }
364 else
365 {
366 got += result;
367 offset += (Offset)result;
368 }
369 } // end while (have < want)
370 return got;
371 } // end NormFile::Read()
372
373 size_t NormFile::Write(const char* buffer, size_t len)
374 {
375 ASSERT(IsOpen());
376 size_t put = 0;
377 while (put < len)
378 {
379 #ifdef WIN32
380 #ifdef _WIN32_WCE
381 size_t result = fwrite(buffer+put, 1, len-put, file_ptr);
382 #else
383 size_t result = _write(fd, buffer+put, (unsigned int)(len-put));
384 #endif // if/else _WIN32_WCE
385 #else
386 size_t result = write(fd, buffer+put, len-put);
387 #endif // if/else WIN32
388 if (result <= 0)
389 {
390 #ifndef _WIN32_WCE
391 if (EINTR != errno)
392 #endif // !_WIN32_WCE
393 {
394 PLOG(PL_FATAL, "NormFile::Write() write(%d) result:%d error: %s\n", len, result, GetErrorString());
395 return 0;
396 }
397 }
398 else
399 {
400 offset += (Offset)result;
401 put += result;
402 }
403 } // end while (put < len)
404 return put;
405 } // end NormFile::Write()
406
407 bool NormFile::Seek(Offset theOffset)
408 {
409 ASSERT(IsOpen());
410 #ifdef WIN32
411 #ifdef _WIN32_WCE
412 // (TBD) properly support big files on WinCE
413 Offset result = fseek(file_ptr, (long)theOffset, SEEK_SET);
414 #else
415 Offset result = _lseeki64(fd, theOffset, SEEK_SET);
416 #endif // if/else _WIN32_WCE
417 #else
418 Offset result = lseek(fd, theOffset, SEEK_SET);
419 #endif // if/else WIN32
420 if (result == (Offset)-1)
421 {
422 PLOG(PL_FATAL, "NormFile::Seek() lseek() error: %s\n", GetErrorString());
423 return false;
424 }
425 else
426 {
427 offset = result;
428 return true; // no error
429 }
430 } // end NormFile::Seek()
431
432 bool NormFile::Pad(Offset theOffset)
433 {
434 if (theOffset > GetSize())
435 {
436 if (Seek(theOffset - 1))
437 {
438 char byte = 0;
439 if (1 != Write(&byte, 1))
440 {
441 PLOG(PL_FATAL, "NormFile::Pad() write error: %s\n", GetErrorString());
442 return false;
443 }
444 }
445 else
446 {
447 PLOG(PL_FATAL, "NormFile::Pad() seek error: %s\n", GetErrorString());
448 return false;
449 }
450 }
451 return true;
452 } // end NormFile::Pad()
453
454 NormFile::Offset NormFile::GetSize() const
455 {
456 ASSERT(IsOpen());
457 #ifdef _WIN32_WCE
458 DWORD fileSize = GetFileSize(_fileno(file_ptr), NULL);
459 return ((Offset)fileSize);
460 #else
461 #ifdef WIN32
462 struct _stati64 info; // instead of "struct _stat"
463 int result = _fstati64(fd, &info); // instead of "_fstat()"
464 #else
465 struct stat info;
466 int result = fstat(fd, &info);
467 #endif // if/else WIN32
468 if (result)
469 {
470 PLOG(PL_FATAL, "Error getting file size: %s\n", GetErrorString());
471 return 0;
472 }
473 else
474 {
475 return info.st_size;
476 }
477 #endif // if/else _WIN32_WCE
478 } // end NormFile::GetSize()
479
480
481 /***********************************************
482 * The NormDirectoryIterator classes is used to
483 * walk directory trees for file transmission
484 */
485
486 NormDirectoryIterator::NormDirectoryIterator()
487 : current(NULL)
488 {
489
490 }
491
492 NormDirectoryIterator::~NormDirectoryIterator()
493 {
494 Close();
495 }
496
497 bool NormDirectoryIterator::Open(const char *thePath)
498 {
499 if (current) Close();
500 #ifdef WIN32
501 #ifdef _WIN32_WCE
502 if (thePath && !NormFile::Exists(thePath))
503 #else
504 if (thePath && _access(thePath, 0))
505 #endif // if/else _WIN32_WCE
506 #else
507 if (thePath && access(thePath, X_OK))
508 #endif // if/else WIN32
509 {
510 PLOG(PL_FATAL, "NormDirectoryIterator: can't access directory: %s\n", thePath);
511 return false;
512 }
513 current = new NormDirectory(thePath);
514 if (current && current->Open())
515 {
516 path_len = (int)strlen(current->Path());
517 path_len = MIN(PATH_MAX, path_len);
518 return true;
519 }
520 else
521 {
522 PLOG(PL_FATAL, "NormDirectoryIterator: can't open directory: %s\n", thePath);
523 if (current) delete current;
524 current = NULL;
525 return false;
526 }
527 } // end NormDirectoryIterator::Init()
528
529 void NormDirectoryIterator::Close()
530 {
531 NormDirectory* d;
532 while ((d = current))
533 {
534 current = d->parent;
535 d->Close();
536 delete d;
537 }
538 } // end NormDirectoryIterator::Close()
539
540 bool NormDirectoryIterator::GetPath(char* pathBuffer)
541 {
542 if (current)
543 {
544 NormDirectory* d = current;
545 while (d->parent) d = d->parent;
546 strncpy(pathBuffer, d->Path(), PATH_MAX);
547 return true;
548 }
549 else
550 {
551 pathBuffer[0] = '\0';
552 return false;
553 }
554 }
555
556 #ifdef WIN32
557 bool NormDirectoryIterator::GetNextFile(char* fileName)
558 {
559 if (!current) return false;
560 bool success = true;
561 while(success)
562 {
563 WIN32_FIND_DATA findData;
564 if (current->hSearch == (HANDLE)-1)
565 {
566 // Construct search string
567 current->GetFullName(fileName);
568 strcat(fileName, "\\*");
569 #ifdef _UNICODE
570 wchar_t wideBuffer[MAX_PATH];
571 mbstowcs(wideBuffer, fileName, MAX_PATH);
572 if ((HANDLE)-1 ==
573 (current->hSearch = FindFirstFile(wideBuffer, &findData)))
574 #else
575 if ((HANDLE)-1 ==
576 (current->hSearch = FindFirstFile(fileName, &findData)))
577 #endif // if/else _UNICODE
578 success = false;
579 else
580 success = true;
581 }
582 else
583 {
584 success = (0 != FindNextFile(current->hSearch, &findData));
585 }
586
587 // Do we have a candidate file?
588 if (success)
589 {
590 #ifdef _UNICODE
591 char cFileName[MAX_PATH];
592 wcstombs(cFileName, findData.cFileName, MAX_PATH);
593 char* ptr = strrchr(cFileName, PROTO_PATH_DELIMITER);
594 #else
595 char* ptr = strrchr(findData.cFileName, PROTO_PATH_DELIMITER);
596 #endif // if/else _UNICODE
597 if (ptr)
598 ptr += 1;
599 else
600 #ifdef _UNICODE
601 ptr = cFileName;
602 #else
603 ptr = findData.cFileName;
604 #endif // if/else _UNICODE
605
606 // Skip "." and ".." directories
607 if (ptr[0] == '.')
608 {
609 if ((1 == strlen(ptr)) ||
610 ((ptr[1] == '.') && (2 == strlen(ptr))))
611 {
612 continue;
613 }
614 }
615 current->GetFullName(fileName);
616 strcat(fileName, ptr);
617 NormFile::Type type = NormFile::GetType(fileName);
618 if (NormFile::NORMAL == type)
619 {
620 size_t nameLen = strlen(fileName);
621 nameLen = MIN(PATH_MAX, nameLen);
622 nameLen -= path_len;
623 memmove(fileName, fileName+path_len, nameLen);
624 if (nameLen < PATH_MAX) fileName[nameLen] = '\0';
625 return true;
626 }
627 else if (NormFile::DIRECTORY == type)
628 {
629
630 NormDirectory *dir = new NormDirectory(ptr, current);
631 if (dir && dir->Open())
632 {
633 // Push sub-directory onto stack and search it
634 current = dir;
635 return GetNextFile(fileName);
636 }
637 else
638 {
639 // Couldn't open try next one
640 if (dir) delete dir;
641 }
642 }
643 else
644 {
645 // NormFile::INVALID file, try next one
646 }
647 } // end if(success)
648 } // end while(success)
649
650 // if parent, popup a level and continue search
651 if (current->parent)
652 {
653 current->Close();
654 NormDirectory *dir = current;
655 current = current->parent;
656 delete dir;
657 return GetNextFile(fileName);
658 }
659 else
660 {
661 current->Close();
662 delete current;
663 current = NULL;
664 return false;
665 }
666 } // end NormDirectoryIterator::GetNextFile() (WIN32)
667 #else
668 bool NormDirectoryIterator::GetNextFile(char* fileName)
669 {
670 if (!current) return false;
671 struct dirent *dp;
672 while((dp = readdir(current->dptr)))
673 {
674 // Make sure it's not "." or ".."
675 if (dp->d_name[0] == '.')
676 {
677 if ((1 == strlen(dp->d_name)) ||
678 ((dp->d_name[1] == '.' ) && (2 == strlen(dp->d_name))))
679 {
680 continue; // skip "." and ".." directory names
681 }
682 }
683 current->GetFullName(fileName);
684 strcat(fileName, dp->d_name);
685 NormFile::Type type = NormFile::GetType(fileName);
686 if (NormFile::NORMAL == type)
687 {
688 int nameLen = strlen(fileName);
689 nameLen = MIN(PATH_MAX, nameLen);
690 nameLen -= path_len;
691 memmove(fileName, fileName+path_len, nameLen);
692 if (nameLen < PATH_MAX) fileName[nameLen] = '\0';
693 return true;
694 }
695 else if (NormFile::DIRECTORY == type)
696 {
697 NormDirectory *dir = new NormDirectory(dp->d_name, current);
698 if (dir && dir->Open())
699 {
700 // Push sub-directory onto stack and search it
701 current = dir;
702 return GetNextFile(fileName);
703 }
704 else
705 {
706 // Couldn't open this one, try next one
707 if (dir) delete dir;
708 }
709 }
710 else
711 {
712 // NormFile::INVALID, try next one
713 }
714 } // end while(readdir())
715
716 // Pop up a level and recursively continue or finish if done
717 if (current->parent)
718 {
719 char path[PATH_MAX];
720 current->parent->GetFullName(path);
721 current->Close();
722 NormDirectory *dir = current;
723 current = current->parent;
724 delete dir;
725 return GetNextFile(fileName);
726 }
727 else
728 {
729 current->Close();
730 delete current;
731 current = NULL;
732 return false; // no more files remain
733 }
734 } // end NormDirectoryIterator::GetNextFile() (UNIX)
735 #endif // if/else WIN32
736
737 NormDirectoryIterator::NormDirectory::NormDirectory(const char* thePath,
738 NormDirectory* theParent)
739 : parent(theParent),
740 #ifdef WIN32
741 hSearch((HANDLE)-1)
742 #else
743 dptr(NULL)
744 #endif // if/else WIN32
745 {
746 strncpy(path, thePath, PATH_MAX);
747 size_t len = MIN(PATH_MAX, strlen(path));
748 if ((len < PATH_MAX) && (PROTO_PATH_DELIMITER != path[len-1]))
749 {
750 path[len++] = PROTO_PATH_DELIMITER;
751 if (len < PATH_MAX) path[len] = '\0';
752 }
753 }
754
755 NormDirectoryIterator::NormDirectory::~NormDirectory()
756 {
757 Close();
758 }
759
760 bool NormDirectoryIterator::NormDirectory::Open()
761 {
762 Close(); // in case it's already open
763 char fullName[PATH_MAX];
764 GetFullName(fullName);
765 // Get rid of trailing PROTO_PATH_DELIMITER
766 size_t len = MIN(PATH_MAX, strlen(fullName));
767 if (PROTO_PATH_DELIMITER == fullName[len-1]) fullName[len-1] = '\0';
768 #ifdef WIN32
769 #ifdef _UNICODE
770 wchar_t wideBuffer[MAX_PATH];
771 mbstowcs(wideBuffer, fullName, MAX_PATH);
772 DWORD attr = GetFileAttributes(wideBuffer);
773 #else
774 DWORD attr = GetFileAttributes(fullName);
775 #endif // if/else _UNICODE
776 if (0xFFFFFFFF == attr)
777 return false;
778 else if (attr & FILE_ATTRIBUTE_DIRECTORY)
779 return true;
780 else
781 return false;
782 #else
783 if((dptr = opendir(fullName)))
784 return true;
785 else
786 return false;
787 #endif // if/else WIN32
788
789 } // end NormDirectoryIterator::NormDirectory::Open()
790
791 void NormDirectoryIterator::NormDirectory::Close()
792 {
793 #ifdef WIN32
794 if (hSearch != (HANDLE)-1)
795 {
796 FindClose(hSearch);
797 hSearch = (HANDLE)-1;
798 }
799 #else
800 if (dptr)
801 {
802 closedir(dptr);
803 dptr = NULL;
804 }
805 #endif // if/else WIN32
806 } // end NormDirectoryIterator::NormDirectory::Close()
807
808
809 void NormDirectoryIterator::NormDirectory::GetFullName(char* ptr)
810 {
811 ptr[0] = '\0';
812 RecursiveCatName(ptr);
813 } // end NormDirectoryIterator::NormDirectory::GetFullName()
814
815 void NormDirectoryIterator::NormDirectory::RecursiveCatName(char* ptr)
816 {
817 if (parent) parent->RecursiveCatName(ptr);
818 size_t len = MIN(PATH_MAX, strlen(ptr));
819 strncat(ptr, path, PATH_MAX-len);
820 } // end NormDirectoryIterator::NormDirectory::RecursiveCatName()
821
822 // Below are some static routines for getting file/directory information
823
824 // Is the named item a valid directory or file (or neither)??
825 NormFile::Type NormFile::GetType(const char* path)
826 {
827 #ifdef WIN32
828 #ifdef _UNICODE
829 wchar_t wideBuffer[MAX_PATH];
830 mbstowcs(wideBuffer, path, MAX_PATH);
831 DWORD attr = GetFileAttributes(wideBuffer);
832 #else
833 DWORD attr = GetFileAttributes(path);
834 #endif // if/else _UNICODE
835 if (0xFFFFFFFF == attr)
836 return INVALID; // error
837 else if (attr & FILE_ATTRIBUTE_DIRECTORY)
838 return DIRECTORY;
839 else
840 return NORMAL;
841 #else
842 struct stat file_info;
843 if (stat(path, &file_info))
844 return INVALID; // stat() error
845 else if ((S_ISDIR(file_info.st_mode)))
846 return DIRECTORY;
847 else
848 return NORMAL;
849 #endif // if/else WIN32
850 } // end NormFile::GetType()
851
852 NormFile::Offset NormFile::GetSize(const char* path)
853 {
854 #ifdef _WIN32_WCE
855 WIN32_FIND_DATA findData;
856 #ifdef _UNICODE
857 wchar_t wideBuffer[MAX_PATH];
858 mbstowcs(wideBuffer, path, MAX_PATH);
859 if (INVALID_HANDLE_VALUE != FindFirstFile(wideBuffer, &findData))
860 #else
861 if (INVALID_HANDLE_VALUE != FindFirstFile(path, &findData))
862 #endif // if/else _UNICODE
863 {
864 Offset fileSize = findData.nFileSizeLow;
865 if (sizeof(Offset) > 4)
866 fileSize |= ((Offset)findData.nFileSizeHigh) << (8*sizeof(DWORD));
867 return fileSize;
868 }
869 else
870 {
871 PLOG(PL_ERROR, "Error getting file size: %s\n", GetErrorString());
872 return 0;
873 }
874 #else
875 #ifdef WIN32
876 struct _stati64 info; // instead of "struct _stat"
877 int result = _stati64(path, &info); // instead of "_stat()"
878 #else
879 struct stat info;
880 int result = stat(path, &info);
881 #endif // if/else WIN32
882 if (result)
883 {
884 //DMSG(0, "Error getting file size: %s\n", GetErrorString());
885 return 0;
886 }
887 else
888 {
889 return info.st_size;
890 }
891 #endif // if/else _WIN32_WCE
892 } // end NormFile::GetSize()
893
894
895 time_t NormFile::GetUpdateTime(const char* path)
896 {
897 #ifdef _WIN32_WCE
898 WIN32_FIND_DATA findData;
899 #ifdef _UNICODE
900 wchar_t wideBuffer[MAX_PATH];
901 mbstowcs(wideBuffer, path, MAX_PATH);
902 if (INVALID_HANDLE_VALUE != FindFirstFile(wideBuffer, &findData))
903 #else
904 if (INVALID_HANDLE_VALUE != FindFirstFile(path, &findData))
905 #endif // if/else _UNICODE
906 {
907 ULARGE_INTEGER ctime = {findData.ftCreationTime.dwLowDateTime,
908 findData.ftCreationTime.dwHighDateTime};
909 ULARGE_INTEGER atime = {findData.ftLastAccessTime.dwLowDateTime,
910 findData.ftLastAccessTime.dwHighDateTime};
911 ULARGE_INTEGER mtime = {findData.ftLastWriteTime.dwLowDateTime,
912 findData.ftLastWriteTime.dwHighDateTime};
913 if (ctime.QuadPart < atime.QuadPart) ctime = atime;
914 if (ctime.QuadPart < mtime.QuadPart) ctime = mtime;
915 const ULARGE_INTEGER epochTime = {0xD53E8000, 0x019DB1DE};
916 ctime.QuadPart -= epochTime.QuadPart;
917 // Convert sytem time to seconds
918 ctime.QuadPart /= 10000000;
919 return ctime.LowPart;
920 }
921 else
922 {
923 PLOG(PL_ERROR, "Error getting file size: %s\n", GetErrorString());
924 return 0;
925 }
926 #else
927 #ifdef WIN32
928 struct _stati64 info; // instead of "struct _stat"
929 int result = _stati64(path, &info); // instead of "_stat()"
930 #else
931 struct stat info;
932 int result = stat(path, &info);
933 #endif // if/else WIN32
934 if (result)
935 {
936 return (time_t)0; // stat() error
937 }
938 else
939 {
940 #ifdef WIN32
941 // Hack because Win2K and Win98 seem to work differently
942 //__time64_t updateTime = MAX(info.st_ctime, info.st_atime);
943 time_t updateTime = MAX(info.st_ctime, info.st_atime);
944 updateTime = MAX(updateTime, info.st_mtime);
945 return ((time_t)updateTime);
946 #else
947 return info.st_ctime;
948 #endif // if/else WIN32
949 }
950 #endif // if/else _WIN32_WCE
951 } // end NormFile::GetUpdateTime()
952
953 bool NormFile::IsLocked(const char* path)
954 {
955 // If file doesn't exist, it's not locked
956 if (!Exists(path)) return false;
957 NormFile testFile;
958 #ifdef WIN32
959 if(!testFile.Open(path, O_WRONLY | O_CREAT))
960 #else
961 if(!testFile.Open(path, O_WRONLY | O_CREAT))
962 #endif // if/else WIN32
963 {
964 return true;
965 }
966 else if (testFile.Lock())
967 {
968 // We were able to lock the file successfully
969 testFile.Unlock();
970 testFile.Close();
971 return false;
972 }
973 else
974 {
975 testFile.Close();
976 return true;
977 }
978 } // end NormFile::IsLocked()
979
980 bool NormFile::Unlink(const char* path)
981 {
982 // Don't unlink a file that is open (locked)
983 if (NormFile::IsLocked(path))
984 {
985 return false;
986 }
987
988 #ifdef _WIN32_WCE
989 #ifdef _UNICODE
990 wchar_t wideBuffer[MAX_PATH];
991 mbstowcs(wideBuffer, path, MAX_PATH);
992 if (0 == DeleteFile(wideBuffer))
993 #else
994 if (0 == DeleteFile(path))
995 #endif // if/else _UNICODE
996 {
997 PLOG(PL_FATAL, "NormFile::Unlink() DeletFile() error: %s\n", GetErrorString());
998 return false;
999 }
1000 #else
1001 #ifdef WIN32
1002 if (_unlink(path))
1003 #else
1004 if (unlink(path))
1005 #endif // if/else WIN32
1006 {
1007 PLOG(PL_FATAL, "NormFile::Unlink() unlink error: %s\n", GetErrorString());
1008 return false;
1009 }
1010 #endif // if/else _WIN32_WCE
1011 else
1012 {
1013 return true;
1014 }
1015
1016 } // end NormFile::Unlink()
1017
1018 NormFileList::NormFileList()
1019 : this_time(0), big_time(0), last_time(0),
1020 updates_only(false), head(NULL), tail(NULL), next(NULL)
1021 {
1022 }
1023
1024 NormFileList::~NormFileList()
1025 {
1026 Destroy();
1027 }
1028
1029 void NormFileList::Destroy()
1030 {
1031 while ((next = head))
1032 {
1033 head = next->next;
1034 delete next;
1035 }
1036 tail = NULL;
1037 } // end NormFileList::Destroy()
1038
1039 bool NormFileList::Append(const char* path)
1040 {
1041 FileItem* theItem = NULL;
1042 switch(NormFile::GetType(path))
1043 {
1044 case NormFile::NORMAL:
1045 theItem = new FileItem(path);
1046 break;
1047 case NormFile::DIRECTORY:
1048 theItem = new DirectoryItem(path);
1049 break;
1050 default:
1051 // Allow non-existent files for update_only mode
1052 // (TBD) allow non-existent directories?
1053 if (updates_only)
1054 {
1055 theItem = new FileItem(path);
1056 }
1057 else
1058 {
1059 PLOG(PL_FATAL, "NormFileList::Append() Bad file/directory name: %s\n",
1060 path);
1061 return false;
1062 }
1063 break;
1064 }
1065 if (theItem)
1066 {
1067 theItem->next = NULL;
1068 if ((theItem->prev = tail))
1069 tail->next = theItem;
1070 else
1071 head = theItem;
1072 tail = theItem;
1073 return true;
1074 }
1075 else
1076 {
1077 PLOG(PL_FATAL, "NormFileList::Append() Error creating file/directory item: %s\n",
1078 GetErrorString());
1079 return false;
1080 }
1081 } // end NormFileList::Append()
1082
1083 bool NormFileList::Remove(const char* path)
1084 {
1085 FileItem* nextItem = head;
1086 size_t pathLen = strlen(path);
1087 pathLen = MIN(pathLen, PATH_MAX);
1088 while (nextItem)
1089 {
1090 size_t nameLen = strlen(nextItem->Path());
1091 nameLen = MIN(nameLen, PATH_MAX);
1092 nameLen = MAX(nameLen, pathLen);
1093 if (!strncmp(path, nextItem->Path(), nameLen))
1094 {
1095 if (nextItem == next) next = nextItem->next;
1096 if (nextItem->prev)
1097 nextItem->prev = next = nextItem->next;
1098 else
1099 head = nextItem->next;
1100 if (nextItem->next)
1101 nextItem->next->prev = nextItem->prev;
1102 else
1103 tail = nextItem->prev;
1104 return true;
1105 }
1106 }
1107 return false;
1108 } // end NormFileList::Remove()
1109
1110 bool NormFileList::GetNextFile(char* pathBuffer)
1111 {
1112 if (!next)
1113 {
1114 next = head;
1115 reset = true;
1116 }
1117 if (next)
1118 {
1119 if (next->GetNextFile(pathBuffer, reset, updates_only,
1120 last_time, this_time, big_time))
1121 {
1122 reset = false;
1123 return true;
1124 }
1125 else
1126 {
1127 if (next->next)
1128 {
1129 next = next->next;
1130 reset = true;
1131 return GetNextFile(pathBuffer);
1132 }
1133 else
1134 {
1135 reset = false;
1136 return false; // end of list
1137 }
1138 }
1139 }
1140 else
1141 {
1142 return false; // empty list
1143 }
1144 } // end NormFileList::GetNextFile()
1145
1146 void NormFileList::GetCurrentBasePath(char* pathBuffer)
1147 {
1148 if (next)
1149 {
1150 if (NormFile::DIRECTORY == next->GetType())
1151 {
1152 strncpy(pathBuffer, next->Path(), PATH_MAX);
1153 size_t len = strlen(pathBuffer);
1154 len = MIN(len, PATH_MAX);
1155 if (PROTO_PATH_DELIMITER != pathBuffer[len-1])
1156 {
1157 if (len < PATH_MAX) pathBuffer[len++] = PROTO_PATH_DELIMITER;
1158 if (len < PATH_MAX) pathBuffer[len] = '\0';
1159 }
1160 }
1161 else // NormFile::NORMAL
1162 {
1163 const char* ptr = strrchr(next->Path(), PROTO_PATH_DELIMITER);
1164 if (ptr++)
1165 {
1166 size_t len = ptr - next->Path();
1167 strncpy(pathBuffer, next->Path(), len);
1168 pathBuffer[len] = '\0';
1169 }
1170 else
1171 {
1172 pathBuffer[0] = '\0';
1173 }
1174 }
1175 }
1176 else
1177 {
1178 pathBuffer[0] = '\0';
1179 }
1180 } // end NormFileList::GetBasePath()
1181
1182
1183 NormFileList::FileItem::FileItem(const char* thePath)
1184 : prev(NULL), next(NULL)
1185 {
1186 size_t len = strlen(thePath);
1187 len = MIN(len, PATH_MAX);
1188 strncpy(path, thePath, PATH_MAX);
1189 size = NormFile::GetSize(thePath);
1190 }
1191
1192 NormFileList::FileItem::~FileItem()
1193 {
1194 }
1195
1196 bool NormFileList::FileItem::GetNextFile(char* thePath,
1197 bool reset,
1198 bool updatesOnly,
1199 time_t lastTime,
1200 time_t thisTime,
1201 time_t& bigTime)
1202 {
1203 if (reset)
1204 {
1205 if (updatesOnly)
1206 {
1207 time_t updateTime = NormFile::GetUpdateTime(thePath);
1208 if (updateTime > bigTime) bigTime = updateTime;
1209 if ((updateTime <= lastTime) || (updateTime > thisTime))
1210 return false;
1211 }
1212 strncpy(thePath, path, PATH_MAX);
1213 return true;
1214 }
1215 else
1216 {
1217 return false;
1218 }
1219 } // end NormFileList::FileItem::GetNextFile()
1220
1221 NormFileList::DirectoryItem::DirectoryItem(const char* thePath)
1222 : NormFileList::FileItem(thePath)
1223 {
1224
1225 }
1226
1227 NormFileList::DirectoryItem::~DirectoryItem()
1228 {
1229 diterator.Close();
1230 }
1231
1232 bool NormFileList::DirectoryItem::GetNextFile(char* thePath,
1233 bool reset,
1234 bool updatesOnly,
1235 time_t lastTime,
1236 time_t thisTime,
1237 time_t& bigTime)
1238 {
1239 if (reset)
1240 {
1241 /* For now we are going to poll all files in a directory individually
1242 since directory update times aren't always changed when files are
1243 are replaced within the directory tree ... uncomment this code
1244 if you only want to check directory nodes that have had their
1245 change time updated
1246 if (updates_only)
1247 {
1248 // Check to see if directory has been touched
1249 time_t update_time = MdpFileGetUpdateTime(path);
1250 if (updateTime > bigTime) *bigTime = updateTime;
1251 if ((updateTime <= lastTime) || (updateTime > thisTime))
1252 return false;
1253 } */
1254 if (!diterator.Open(path))
1255 {
1256 PLOG(PL_FATAL, "NormFileList::DirectoryItem::GetNextFile() Directory iterator init error\n");
1257 return false;
1258 }
1259 }
1260 strncpy(thePath, path, PATH_MAX);
1261 size_t len = strlen(thePath);
1262 len = MIN(len, PATH_MAX);
1263 if ((PROTO_PATH_DELIMITER != thePath[len-1]) && (len < PATH_MAX))
1264 {
1265 thePath[len++] = PROTO_PATH_DELIMITER;
1266 if (len < PATH_MAX) thePath[len] = '\0';
1267 }
1268 char tempPath[PATH_MAX];
1269 while (diterator.GetNextFile(tempPath))
1270 {
1271 size_t maxLen = PATH_MAX - len;
1272 strncat(thePath, tempPath, maxLen);
1273 if (updatesOnly)
1274 {
1275 time_t updateTime = NormFile::GetUpdateTime(thePath);
1276 if (updateTime > bigTime) bigTime = updateTime;
1277 if ((updateTime <= lastTime) || (updateTime > thisTime))
1278 {
1279 thePath[len] = '\0';
1280 continue;
1281 }
1282 }
1283 return true;
1284 }
1285 return false;
1286 } // end NormFileList::DirectoryItem::GetNextFile()
1287