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