blob: e3e49478b511a279ce2772ab62d1b182b0377f01 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39// needed for GNU/LINUX glibc otherwise pread/pwrite wont work
40
41#ifdef __linux__
42#ifndef _XOPEN_SOURCE
43#define _XOPEN_SOURCE 500
44#endif
45/*
46 * on old glibc's, this has to be
47 * defined explicitly
48 */
49#ifndef _XOPEN_SOURCE_EXTENDED
50#define _XOPEN_SOURCE_EXTENDED
51#endif
52#endif
53
54#include <cc++/config.h>
55#include <cc++/export.h>
56#include <cc++/exception.h>
57#include <cc++/thread.h>
58#include <cc++/file.h>
59#include <cc++/process.h>
60#include "private.h"
61
62#ifdef __BORLANDC__
63#include <stdio.h>
64#include <stdlib.h>
65#else
66#include <cstdio>
67#include <cstdlib>
68#endif
69#include <sys/stat.h>
70#include <cerrno>
71
72#ifndef WIN32
73
74#ifdef HAVE_SYS_PARAM_H
75#include <sys/param.h>
76#endif
77
78#ifdef HAVE_SYS_FILE_H
79#include <sys/file.h>
80#endif
81
82#ifdef HAVE_SYS_LOCKF_H
83#include <sys/lockf.h>
84#endif
85
86#ifdef COMMON_AIX_FIXES
87#undef LOCK_EX
88#undef LOCK_SH
89#endif
90
91#ifdef MACOSX
92#define MISSING_LOCKF
93#endif
94
95#ifndef F_LOCK
96#define MISSING_LOCKF
97
98enum {
99 F_ULOCK = 1,
100 F_LOCK,
101 F_TLOCK,
102 F_TEST
103};
104
105#endif
106
107#endif // ndef WIN32
108
109#if defined(_OSF_SOURCE) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE > 1
110#undef LOCK_EX
111#undef LOCK_SH
112#endif
113
114#if 0
115/*
116 * not used anymore ? (hen)
117 */
118static const char *clearfile(const char *pathname)
119{
120 remove(pathname);
121 return pathname;
122}
123
124static const char *clearfifo(const char *pathname, int mode)
125{
126 remove(pathname);
127 mkfifo(pathname, mode);
128 return pathname;
129}
130#endif
131
132#ifdef CCXX_NAMESPACES
133namespace ost {
134#endif
135
136RandomFile::RandomFile(const char *name) : Mutex(name)
137{
138#ifdef WIN32
139 fd = INVALID_HANDLE_VALUE;
140 // immediate is not defined on Win32
141#else
142 fd = -1;
143 flags.immediate = false;
144#endif
145 flags.thrown = flags.initial = flags.temp = false;
146 flags.count = 0;
147 pathname = NULL;
148}
149
150RandomFile::RandomFile(const RandomFile &rf) : Mutex()
151{
152 // first, `dup'-licate the file descriptor/handle
153#ifdef WIN32
154 HANDLE pidHandle = GetCurrentProcess();
155 HANDLE dupHandle;
156
157 if(rf.fd != INVALID_HANDLE_VALUE) {
158 if(!DuplicateHandle(pidHandle, rf.fd, pidHandle, &dupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
159 fd = INVALID_HANDLE_VALUE;
160 else
161 fd = dupHandle;
162 }
163 else
164 fd = INVALID_HANDLE_VALUE;
165
166#else
167 if(rf.fd > -1)
168 fd = dup(rf.fd);
169 else
170 fd = -1;
171
172#endif
173
174 flags = rf.flags;
175 flags.count = 0;
176
177 if(rf.pathname)
178 pathname = newString(rf.pathname);
179 else
180 pathname = NULL;
181}
182
183RandomFile::~RandomFile()
184{
185 final();
186}
187
188File::Error RandomFile::restart(void)
189{
190 return errOpenFailed;
191}
192
193File::Attr RandomFile::initialize(void)
194{
195 return attrPublic;
196}
197
198void RandomFile::final(void)
199{
200#ifdef WIN32
201 if(fd != INVALID_HANDLE_VALUE) {
202 CloseHandle(fd);
203 if(flags.temp && pathname)
204 DeleteFile(pathname);
205 }
206
207#else
208 if(fd > -1) {
209 close(fd);
210 if(flags.temp && pathname)
211 remove(pathname);
212 }
213#endif
214
215 if(pathname) {
216 delString(pathname);
217 pathname = NULL;
218 }
219
220#ifdef WIN32
221 fd = INVALID_HANDLE_VALUE;
222#else
223 fd = -1;
224#endif
225 flags.count = 0;
226 flags.initial = false;
227}
228
229RandomFile::Error RandomFile::error(Error id, char *str)
230{
231 errstr = str;
232 errid = id;
233 if(!flags.thrown) {
234 flags.thrown = true;
235#ifdef CCXX_EXCEPTIONS
236 if(Thread::getException() == Thread::throwObject)
237 throw(this);
238#ifdef COMMON_STD_EXCEPTION
239 else if(Thread::getException() == Thread::throwException) {
240 if(!str)
241 str = (char *)"";
242 throw FileException(str);
243 }
244#endif
245#endif
246 }
247 return id;
248}
249
250bool RandomFile::initial(void)
251{
252 bool init;
253
254#ifdef WIN32
255 if(fd == INVALID_HANDLE_VALUE)
256#else
257 if(fd < 0)
258#endif
259 return false;
260
261 enterMutex();
262 init = flags.initial;
263 flags.initial = false;
264
265 if(!init) {
266 leaveMutex();
267 return false;
268 }
269
270#ifdef WIN32
271 Attr access = initialize();
272 if(access == attrInvalid) {
273 CloseHandle(fd);
274 if(pathname)
275 DeleteFile(pathname);
276 fd = INVALID_HANDLE_VALUE;
277 leaveMutex();
278 error(errInitFailed);
279 return false;
280 }
281
282#else
283 int mode = (int)initialize();
284 if(!mode) {
285 close(fd);
286 fd = -1;
287 if(pathname)
288 remove(pathname);
289 leaveMutex();
290 error(errInitFailed);
291 return false;
292 }
293 fchmod(fd, mode);
294#endif
295
296 leaveMutex();
297 return init;
298}
299
300#ifndef WIN32
301RandomFile::Error RandomFile::setCompletion(Complete mode)
302{
303 long flag = fcntl(fd, F_GETFL);
304
305 if(fd < 0)
306 return errNotOpened;
307
308 flags.immediate = false;
309#ifdef O_SYNC
310 flag &= ~(O_SYNC | O_NDELAY);
311#else
312 flag &= ~O_NDELAY;
313#endif
314 switch(mode) {
315 case completionImmediate:
316#ifdef O_SYNC
317 flag |= O_SYNC;
318#endif
319 flags.immediate = true;
320 break;
321
322 case completionDelayed:
323 flag |= O_NDELAY;
324
325 //completionDeferred: ? (hen)
326 case completionDeferred:
327 break;
328 }
329 fcntl(fd, F_SETFL, flag);
330 return errSuccess;
331}
332#endif
333
334off_t RandomFile::getCapacity(void)
335{
336 off_t eof, pos = 0;
337#ifdef WIN32
338 if(!fd)
339#else
340 if(fd < 0)
341#endif
342 return 0;
343
344 enterMutex();
345#ifdef WIN32
346 pos = SetFilePointer(fd, 0l, NULL, FILE_CURRENT);
347 eof = SetFilePointer(fd, 0l, NULL, FILE_END);
348 SetFilePointer(fd, pos, NULL, FILE_BEGIN);
349#else
350 lseek(fd, pos, SEEK_SET);
351 pos = lseek(fd, 0l, SEEK_CUR);
352 eof = lseek(fd, 0l, SEEK_END);
353#endif
354 leaveMutex();
355 return eof;
356}
357
358bool RandomFile::operator!(void)
359{
360#ifdef WIN32
361 return fd == INVALID_HANDLE_VALUE;
362#else
363 if(fd < 0)
364 return true;
365
366 return false;
367#endif
368}
369
370ThreadFile::ThreadFile(const char *path) : RandomFile(path)
371{
372 first = NULL;
373 open(path);
374}
375
376ThreadFile::~ThreadFile()
377{
378 final();
379 fcb_t *next;
380 while(first) {
381 next = first->next;
382 delete first;
383 first = next;
384 }
385}
386
387ThreadFile::Error ThreadFile::restart(void)
388{
389 return open(pathname);
390}
391
392ThreadFile::Error ThreadFile::open(const char *path)
393{
394#ifdef WIN32
395 if(fd != INVALID_HANDLE_VALUE)
396#else
397 if(fd > -1)
398#endif
399 final();
400
401
402 if(path != pathname) {
403 if(pathname)
404 delString(pathname);
405 pathname = newString(path);
406 }
407
408 flags.initial = false;
409
410#ifdef WIN32
411 fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
412 if(fd == INVALID_HANDLE_VALUE) {
413 flags.initial = true;
414 fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
415 }
416 if(fd == INVALID_HANDLE_VALUE)
417 return errOpenFailed;
418
419#else
420 fd = ::open(pathname, O_RDWR);
421 if(fd < 0) {
422 flags.initial = true;
423 fd = ::open(pathname, O_CREAT | O_RDWR | O_TRUNC,
424 (int)attrPrivate);
425 }
426 if(fd < 0)
427 return error(errOpenFailed);
428
429#ifdef LOCK_EX
430 if(flock(fd, LOCK_EX | LOCK_NB)) {
431 close(fd);
432 fd = -1;
433 return error(errOpenInUse);
434 }
435#endif
436#endif // WIN32
437
438 return errSuccess;
439}
440
441ThreadFile::fcb_t *ThreadFile::getFCB(void)
442{
443 fcb_t *fcb = (fcb_t *)state.getKey();
444
445 if(!fcb) {
446 fcb = new fcb_t;
447 fcb->next = first;
448 first = fcb;
449 fcb->address = NULL;
450 fcb->len = 0;
451 fcb->pos = 0;
452 state.setKey(fcb);
453 }
454 return fcb;
455}
456
457ThreadFile::Error ThreadFile::fetch(caddr_t address, ccxx_size_t len, off_t pos)
458{
459 fcb_t *fcb = getFCB();
460#ifdef WIN32
461 Thread::Cancel save;
462 if(fd == INVALID_HANDLE_VALUE)
463#else
464 if(fd < 0)
465#endif
466 return errNotOpened;
467
468 if(address)
469 fcb->address = address;
470
471 if(len)
472 fcb->len = len;
473
474 if(pos != -1)
475 fcb->pos = pos;
476
477
478#ifdef WIN32
479 enterMutex();
480 save = Thread::enterCancel();
481 SetFilePointer(fd, fcb->pos, NULL, FILE_BEGIN);
482 DWORD count;
483 if(!ReadFile(fd, fcb->address, fcb->len, &count, NULL)) {
484 Thread::exitCancel(save);
485 leaveMutex();
486 return errReadFailure;
487 }
488 Thread::exitCancel(save);
489 leaveMutex();
490 if(count < fcb->len)
491 return errReadIncomplete;
492
493 return errSuccess;
494
495#else
496#ifdef HAVE_PREAD_PWRITE
497 int io = ::pread(fd, fcb->address, fcb->len, fcb->pos);
498#else
499 enterMutex();
500 lseek(fd, fcb->pos, SEEK_SET);
501 int io = ::read(fd, fcb->address, fcb->len);
502 leaveMutex();
503#endif
504
505 if((size_t) io == fcb->len)
506 return errSuccess;
507
508 if(io > -1)
509 return errReadIncomplete;
510
511 switch(errno) {
512 case EINTR:
513 return errReadInterrupted;
514 default:
515 return errReadFailure;
516 }
517#endif // WIN32
518}
519
520ThreadFile::Error ThreadFile::update(caddr_t address, ccxx_size_t len, off_t pos)
521{
522 fcb_t *fcb = getFCB();
523#ifdef WIN32
524 if(fd == INVALID_HANDLE_VALUE)
525#else
526 if(fd < 0)
527#endif
528 return errNotOpened;
529
530
531 if(address)
532 fcb->address = address;
533
534 if(len)
535 fcb->len = len;
536
537 if(pos != -1)
538 fcb->pos = pos;
539
540#ifdef WIN32
541 enterMutex();
542 Thread::Cancel save = Thread::enterCancel();
543 SetFilePointer(fd, fcb->pos, NULL, FILE_BEGIN);
544 DWORD count;
545 if(!WriteFile(fd, fcb->address, fcb->len, &count, NULL)) {
546 Thread::exitCancel(save);
547 leaveMutex();
548 return errWriteFailure;
549 }
550 Thread::exitCancel(save);
551 leaveMutex();
552 if(count < fcb->len)
553 return errWriteIncomplete;
554
555 return errSuccess;
556
557#else
558#ifdef HAVE_PREAD_PWRITE
559 int io = ::pwrite(fd, fcb->address, fcb->len, fcb->pos);
560#else
561 enterMutex();
562 lseek(fd, fcb->pos, SEEK_SET);
563 int io = ::write(fd, fcb->address, fcb->len);
564 leaveMutex();
565#endif
566
567 if((size_t) io == fcb->len)
568 return errSuccess;
569
570 if(io > -1)
571 return errWriteIncomplete;
572
573 switch(errno) {
574 case EINTR:
575 return errWriteInterrupted;
576 default:
577 return errWriteFailure;
578 }
579#endif //WIN32
580}
581
582ThreadFile::Error ThreadFile::append(caddr_t address, ccxx_size_t len)
583{
584 fcb_t *fcb = getFCB();
585#ifdef WIN32
586 if(fd == INVALID_HANDLE_VALUE)
587#else
588 if(fd < 0)
589#endif
590 return errNotOpened;
591
592 if(address)
593 fcb->address = address;
594
595 if(len)
596 fcb->len = len;
597
598 enterMutex();
599
600#ifdef WIN32
601 Thread::Cancel save = Thread::enterCancel();
602 fcb->pos = SetFilePointer(fd, 0l, NULL, FILE_END);
603 DWORD count;
604 if(!WriteFile(fd, fcb->address, fcb->len, &count, NULL)) {
605 Thread::exitCancel(save);
606 leaveMutex();
607 return errWriteFailure;
608 }
609 Thread::exitCancel(save);
610 leaveMutex();
611 if(count < fcb->len)
612 return errWriteIncomplete;
613
614 return errSuccess;
615
616#else
617 fcb->pos = lseek(fd, 0l, SEEK_END);
618 int io = ::write(fd, fcb->address, fcb->len);
619 leaveMutex();
620
621 if((size_t) io == fcb->len)
622 return errSuccess;
623
624 if(io > -1)
625 return errWriteIncomplete;
626
627 switch(errno) {
628 case EINTR:
629 return errWriteInterrupted;
630 default:
631 return errWriteFailure;
632 }
633#endif // WIN32
634}
635
636off_t ThreadFile::getPosition(void)
637{
638 fcb_t *fcb = getFCB();
639
640 return fcb->pos;
641}
642
643bool ThreadFile::operator++(void)
644{
645 off_t eof;
646 fcb_t *fcb = getFCB();
647
648 fcb->pos += fcb->len;
649 enterMutex();
650#ifdef WIN32
651 eof = SetFilePointer(fd, 0l, NULL, FILE_END);
652#else
653 eof = lseek(fd, 0l, SEEK_END);
654#endif
655 leaveMutex();
656
657 if(fcb->pos >= eof) {
658 fcb->pos = eof;
659 return true;
660 }
661 return false;
662}
663
664bool ThreadFile::operator--(void)
665{
666 fcb_t *fcb = getFCB();
667
668 fcb->pos -= fcb->len;
669 if(fcb->pos <= 0) {
670 fcb->pos = 0;
671 return true;
672 }
673 return false;
674}
675
676SharedFile::SharedFile(const char *path) :
677RandomFile(path)
678{
679 fcb.address = NULL;
680 fcb.len = 0;
681 fcb.pos = 0;
682 open(path);
683}
684
685SharedFile::SharedFile(const SharedFile &sh) :
686RandomFile(sh)
687{
688}
689
690SharedFile::~SharedFile()
691{
692 final();
693}
694
695SharedFile::Error SharedFile::open(const char *path)
696{
697#ifdef WIN32
698 if(fd != INVALID_HANDLE_VALUE)
699#else
700 if(fd > -1)
701#endif
702 final();
703
704 if(path != pathname) {
705 if(pathname)
706 delString(pathname);
707 pathname = newString(path);
708 }
709
710 flags.initial = false;
711
712#ifdef WIN32
713 fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
714 if(fd == INVALID_HANDLE_VALUE) {
715 flags.initial = true;
716 fd = CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
717 }
718 if(fd == INVALID_HANDLE_VALUE)
719 return errOpenFailed;
720
721 return errSuccess;
722
723#else
724 fd = ::open(pathname, O_RDWR);
725 if(fd < 0) {
726 flags.initial = true;
727 fd = ::open(pathname, O_CREAT | O_RDWR | O_TRUNC,
728 (int)attrPrivate);
729 }
730 if(fd < 0)
731 return error(errOpenFailed);
732
733#ifdef LOCK_SH
734 if(flock(fd, LOCK_SH | LOCK_NB)) {
735 close(fd);
736 fd = -1;
737 return error(errOpenInUse);
738 }
739#endif
740 return errSuccess;
741#endif // WIN32
742}
743
744SharedFile::Error SharedFile::fetch(caddr_t address, ccxx_size_t len, off_t pos)
745{
746#ifdef WIN32
747 if(fd == INVALID_HANDLE_VALUE)
748#else
749 if(fd < 0)
750#endif
751 return errNotOpened;
752
753 enterMutex();
754 if(address)
755 fcb.address = address;
756
757 if(len)
758 fcb.len = len;
759
760 if(pos != -1)
761 fcb.pos = pos;
762
763#ifdef WIN32
764 Thread::Cancel save = Thread::enterCancel();
765 OVERLAPPED over;
766 SetFilePointer(fd, fcb.pos, NULL, FILE_BEGIN);
767 over.hEvent = 0;
768 over.Offset = fcb.pos;
769 over.OffsetHigh = 0;
770 LockFileEx(fd, LOCKFILE_EXCLUSIVE_LOCK, 0, fcb.len, 0, &over);
771 DWORD count;
772 if(!ReadFile(fd, fcb.address, fcb.len, &count, NULL)) {
773 Thread::exitCancel(save);
774 leaveMutex();
775 return errReadFailure;
776 }
777 Thread::exitCancel(save);
778 leaveMutex();
779 if(count < fcb.len)
780 return errReadIncomplete;
781
782 return errSuccess;
783
784#else
785 lseek(fd, fcb.pos, SEEK_SET);
786 if(lockf(fd, F_LOCK, fcb.len)) {
787 leaveMutex();
788 return errLockFailure;
789 }
790
791 int io = ::read(fd, fcb.address, fcb.len);
792 leaveMutex();
793
794 if((size_t) io == fcb.len)
795 return errSuccess;
796
797 if(io > -1)
798 return errReadIncomplete;
799
800 switch(errno) {
801 case EINTR:
802 return errReadInterrupted;
803 default:
804 return errReadFailure;
805 }
806#endif
807}
808
809#ifndef WIN32
810SharedFile::Error SharedFile::clear(ccxx_size_t len, off_t pos)
811{
812 if(fd < 0)
813 return errNotOpened;
814
815 enterMutex();
816 if(len)
817 fcb.len = len;
818
819 if(pos != -1)
820 fcb.pos = pos;
821
822 lseek(fd, fcb.pos, SEEK_SET);
823 if(lockf(fd, F_ULOCK, fcb.len)) {
824 leaveMutex();
825 return errLockFailure;
826 }
827 leaveMutex();
828 return errSuccess;
829}
830#endif // ndef WIN32
831
832SharedFile::Error SharedFile::update(caddr_t address, ccxx_size_t len, off_t pos)
833{
834#ifdef WIN32
835 if(fd == INVALID_HANDLE_VALUE)
836#else
837 if(fd < 0)
838#endif
839 return errNotOpened;
840
841 enterMutex();
842 if(address)
843 fcb.address = address;
844
845 if(len)
846 fcb.len = len;
847
848 if(pos != -1)
849 fcb.pos = pos;
850
851#ifdef WIN32
852 Thread::Cancel save = Thread::enterCancel();
853 OVERLAPPED over;
854 SetFilePointer(fd, fcb.pos, NULL, FILE_BEGIN);
855 over.hEvent = 0;
856 over.Offset = pos;
857 over.OffsetHigh = 0;
858 DWORD count;
859 if(!WriteFile(fd, fcb.address, fcb.len, &count, NULL)) {
860 SetFilePointer(fd, fcb.pos, NULL, FILE_CURRENT);
861 UnlockFileEx(fd, 0, len, 0, &over);
862 Thread::exitCancel(save);
863 leaveMutex();
864 return errWriteFailure;
865 }
866 SetFilePointer(fd, fcb.pos, NULL, FILE_CURRENT);
867 UnlockFileEx(fd, 0, len, 0, &over);
868 Thread::exitCancel(save);
869 leaveMutex();
870 if(count < fcb.len)
871 return errWriteIncomplete;
872
873 return errSuccess;
874
875#else
876 lseek(fd, fcb.pos, SEEK_SET);
877 int io = ::write(fd, fcb.address, fcb.len);
878 if(lockf(fd, F_ULOCK, fcb.len)) {
879 leaveMutex();
880 return errLockFailure;
881 }
882 leaveMutex();
883
884 if((size_t) io == fcb.len)
885 return errSuccess;
886
887 if(io > -1)
888 return errWriteIncomplete;
889
890 switch(errno) {
891 case EINTR:
892 return errWriteInterrupted;
893 default:
894 return errWriteFailure;
895 }
896#endif // WIN32
897}
898
899SharedFile::Error SharedFile::append(caddr_t address, ccxx_size_t len)
900{
901#ifdef WIN32
902 if(fd == INVALID_HANDLE_VALUE)
903#else
904 if(fd < 0)
905#endif
906 return errNotOpened;
907
908 enterMutex();
909 if(address)
910 fcb.address = address;
911
912 if(len)
913 fcb.len = len;
914
915#ifdef WIN32
916 Thread::Cancel save = Thread::enterCancel();
917 fcb.pos = SetFilePointer(fd, 0l, NULL, FILE_END);
918 OVERLAPPED over;
919 over.hEvent = 0;
920 over.Offset = fcb.pos;
921 over.OffsetHigh = 0;
922 LONG eof = fcb.pos;
923 LockFileEx(fd, LOCKFILE_EXCLUSIVE_LOCK, 0, 0x7fffffff, 0, &over);
924 fcb.pos = SetFilePointer(fd, 0l, NULL, FILE_END);
925 DWORD count;
926 if(!WriteFile(fd, fcb.address, fcb.len, &count, NULL)) {
927 SetFilePointer(fd, eof, NULL, FILE_CURRENT);
928 Thread::exitCancel(save);
929 UnlockFileEx(fd, 0, 0x7fffffff, 0, &over);
930 Thread::exitCancel(save);
931 leaveMutex();
932 return errWriteFailure;
933 }
934 SetFilePointer(fd, eof, NULL, FILE_CURRENT);
935 UnlockFileEx(fd, 0, 0x7fffffff, 0, &over);
936 Thread::exitCancel(save);
937 leaveMutex();
938 if(count < fcb.len)
939 return errWriteIncomplete;
940
941 return errSuccess;
942
943#else
944 fcb.pos = lseek(fd, 0l, SEEK_END);
945 if(lockf(fd, F_LOCK, -1)) {
946 leaveMutex();
947 return errLockFailure;
948 }
949 fcb.pos = lseek(fd, 0l, SEEK_END);
950 int io = ::write(fd, fcb.address, fcb.len);
951 lseek(fd, fcb.pos, SEEK_SET);
952 if(lockf(fd, F_ULOCK, -1)) {
953 leaveMutex();
954 return errLockFailure;
955 }
956 leaveMutex();
957
958 if((size_t) io == fcb.len)
959 return errSuccess;
960
961 if(io > -1)
962 return errWriteIncomplete;
963
964 switch(errno) {
965 case EINTR:
966 return errWriteInterrupted;
967 default:
968 return errWriteFailure;
969 }
970#endif // WIN32
971}
972
973off_t SharedFile::getPosition(void)
974{
975 return fcb.pos;
976}
977
978bool SharedFile::operator++(void)
979{
980 off_t eof;
981
982 enterMutex();
983 fcb.pos += fcb.len;
984#ifdef WIN32
985 eof = SetFilePointer(fd, 0l, NULL, FILE_END);
986#else
987 eof = lseek(fd, 0l, SEEK_END);
988#endif
989
990 if(fcb.pos >= eof) {
991 fcb.pos = eof;
992 leaveMutex();
993 return true;
994 }
995 leaveMutex();
996 return false;
997}
998
999bool SharedFile::operator--(void)
1000{
1001 enterMutex();
1002 fcb.pos -= fcb.len;
1003 if(fcb.pos <= 0) {
1004 fcb.pos = 0;
1005 leaveMutex();
1006 return true;
1007 }
1008 leaveMutex();
1009 return false;
1010}
1011
1012size_t MappedFile::pageAligned(size_t size)
1013{
1014 size_t pages = size / Process::getPageSize();
1015
1016 if(size % Process::getPageSize())
1017 ++pages;
1018
1019 return pages * Process::getPageSize();
1020}
1021
1022#ifdef WIN32
1023
1024static void makemapname(const char *source, char *target)
1025{
1026 unsigned count = 60;
1027 while(*source && count--) {
1028 if(*source == '/' || *source == '\\')
1029 *(target++) = '_';
1030 else
1031 *(target++) = toupper(*source);
1032 ++source;
1033 }
1034 *target = 0;
1035}
1036
1037MappedFile::MappedFile(const char *fname, Access mode, size_t size) :
1038RandomFile(fname)
1039{
1040 DWORD share, page;
1041 map = INVALID_HANDLE_VALUE;
1042 fcb.address = NULL;
1043
1044 switch(mode) {
1045 case accessReadOnly:
1046 share = FILE_SHARE_READ;
1047 page = PAGE_READONLY;
1048 prot = FILE_MAP_READ;
1049 break;
1050 case accessWriteOnly:
1051 share = FILE_SHARE_WRITE;
1052 page = PAGE_WRITECOPY;
1053 prot = FILE_MAP_COPY;
1054 break;
1055 case accessReadWrite:
1056 share = FILE_SHARE_READ|FILE_SHARE_WRITE;
1057 page = PAGE_READWRITE;
1058 prot = FILE_MAP_WRITE;
1059 }
1060 fd = CreateFile(pathname, mode, share, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
1061 if(fd == INVALID_HANDLE_VALUE) {
1062 error(errOpenFailed);
1063 return;
1064 }
1065 SetFilePointer(fd, (LONG)size, 0, FILE_BEGIN);
1066 SetEndOfFile(fd);
1067 makemapname(fname, mapname);
1068 map = CreateFileMapping(fd, NULL, page, 0, 0, mapname);
1069 if(!map)
1070 error(errMapFailed);
1071 fcb.address = MapViewOfFile(map, prot, 0, 0, size);
1072 fcb.len = (ccxx_size_t)size;
1073 fcb.pos = 0;
1074 if(!fcb.address)
1075 error(errMapFailed);
1076}
1077
1078MappedFile::MappedFile(const char *fname, Access mode) :
1079RandomFile(fname)
1080{
1081 DWORD share, page;
1082 map = INVALID_HANDLE_VALUE;
1083 fcb.address = NULL;
1084
1085 switch(mode) {
1086 case accessReadOnly:
1087 share = FILE_SHARE_READ;
1088 page = PAGE_READONLY;
1089 prot = FILE_MAP_READ;
1090 break;
1091 case accessWriteOnly:
1092 share = FILE_SHARE_WRITE;
1093 page = PAGE_WRITECOPY;
1094 prot = FILE_MAP_COPY;
1095 break;
1096 case accessReadWrite:
1097 share = FILE_SHARE_READ|FILE_SHARE_WRITE;
1098 page = PAGE_READWRITE;
1099 prot = FILE_MAP_WRITE;
1100 }
1101 fd = CreateFile(pathname, mode, share, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
1102 if(fd == INVALID_HANDLE_VALUE) {
1103 error(errOpenFailed);
1104 return;
1105 }
1106 makemapname(fname, mapname);
1107 map = CreateFileMapping(fd, NULL, page, 0, 0, mapname);
1108 if(!map)
1109 error(errMapFailed);
1110}
1111
1112MappedFile::MappedFile(const char *fname, pos_t pos, size_t len, Access mode) :
1113RandomFile(fname)
1114{
1115 DWORD share, page;
1116 map = INVALID_HANDLE_VALUE;
1117 fcb.address = NULL;
1118
1119 switch(mode) {
1120 case accessReadOnly:
1121 share = FILE_SHARE_READ;
1122 page = PAGE_READONLY;
1123 prot = FILE_MAP_READ;
1124 break;
1125 case accessWriteOnly:
1126 share = FILE_SHARE_WRITE;
1127 page = PAGE_WRITECOPY;
1128 prot = FILE_MAP_COPY;
1129 break;
1130 case accessReadWrite:
1131 share = FILE_SHARE_READ|FILE_SHARE_WRITE;
1132 page = PAGE_READWRITE;
1133 prot = FILE_MAP_WRITE;
1134 }
1135 fd = CreateFile(pathname, mode, share, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
1136 if(fd == INVALID_HANDLE_VALUE) {
1137 error(errOpenFailed);
1138 return;
1139 }
1140 makemapname(fname, mapname);
1141 map = CreateFileMapping(fd, NULL, page, 0, 0, mapname);
1142 if(!map) {
1143 error(errMapFailed);
1144 return;
1145 }
1146 fcb.address = MapViewOfFile(map, prot, 0, pos, len);
1147 fcb.len = (ccxx_size_t)len;
1148 fcb.pos = pos;
1149 if(!fcb.address)
1150 error(errMapFailed);
1151}
1152
1153MappedFile::~MappedFile()
1154{
1155 if(fcb.address) {
1156 unlock();
1157 UnmapViewOfFile(fcb.address);
1158 }
1159
1160 if(map != INVALID_HANDLE_VALUE)
1161 CloseHandle(map);
1162
1163 final();
1164}
1165
1166void MappedFile::sync(void)
1167{
1168}
1169
1170void MappedFile::sync(caddr_t address, size_t len)
1171{
1172}
1173
1174void MappedFile::release(caddr_t address, size_t len)
1175{
1176 if(fcb.address) {
1177 unlock();
1178 UnmapViewOfFile(fcb.address);
1179 }
1180 fcb.address = NULL;
1181}
1182
1183caddr_t MappedFile::fetch(off_t pos, size_t len)
1184{
1185 if(fcb.address) {
1186 unlock();
1187 UnmapViewOfFile(fcb.address);
1188 }
1189
1190 fcb.address = MapViewOfFile(map, prot, 0, pos, len);
1191 fcb.len = (ccxx_size_t)len;
1192 fcb.pos = pos;
1193 if(!fcb.address)
1194 error(errMapFailed);
1195
1196 return fcb.address;
1197}
1198
1199void MappedFile::update(size_t offset, size_t len)
1200{
1201}
1202
1203void MappedFile::update(caddr_t address, size_t len)
1204{
1205}
1206
1207bool MappedFile::lock(void)
1208{
1209 unlock();
1210 if(VirtualLock(fcb.address, fcb.len))
1211 fcb.locked = true;
1212 return fcb.locked;
1213}
1214
1215void MappedFile::unlock(void)
1216{
1217 if(!fcb.address)
1218 fcb.locked = false;
1219
1220 if(!fcb.locked)
1221 return;
1222
1223 VirtualUnlock(fcb.address, fcb.len);
1224 fcb.locked = false;
1225}
1226
1227#else
1228#ifdef HAVE_MLOCK
1229MappedFile::MappedFile(const char *fname, Access mode) :
1230RandomFile(fname)
1231{
1232 fd = open(fname, (int)mode);
1233 if(fd < 0 && mode != accessReadOnly)
1234 fd = ::open(pathname, O_CREAT | O_RDWR | O_TRUNC,
1235 (int)attrPrivate);
1236
1237 if(fd < 0) {
1238 error(errOpenFailed);
1239 return;
1240 }
1241
1242 switch(mode) {
1243 case O_RDONLY:
1244 prot = PROT_READ;
1245 break;
1246 case O_WRONLY:
1247 prot = PROT_WRITE;
1248 break;
1249 default:
1250 prot = PROT_READ | PROT_WRITE;
1251 }
1252}
1253
1254MappedFile::MappedFile(const char *fname, Access mode, size_t size) :
1255RandomFile(fname)
1256{
1257 fd = open(fname, (int)mode | O_CREAT, 0660);
1258
1259 if(fd < 0) {
1260 error(errOpenFailed);
1261 return;
1262 }
1263
1264 switch(mode) {
1265 case O_RDONLY:
1266 prot = PROT_READ;
1267 break;
1268 case O_WRONLY:
1269 prot = PROT_WRITE;
1270 break;
1271 default:
1272 prot = PROT_READ | PROT_WRITE;
1273 }
1274
1275 enterMutex();
1276 lseek(fd, size, SEEK_SET);
1277 fcb.address = (caddr_t)mmap(NULL, size, prot, MAP_SHARED, fd, 0);
1278 fcb.len = size;
1279 fcb.pos = 0;
1280 leaveMutex();
1281 if((caddr_t)(fcb.address) == (caddr_t)(MAP_FAILED)) {
1282 close(fd);
1283 fd = -1;
1284 error(errMapFailed);
1285 }
1286}
1287
1288MappedFile::MappedFile(const char *fname, pos_t pos, size_t len, Access mode) :
1289RandomFile(fname)
1290{
1291 fd = open(fname, (int)mode);
1292 if(fd < 0) {
1293 error(errOpenFailed);
1294 return;
1295 }
1296
1297 switch(mode) {
1298 case O_RDONLY:
1299 prot = PROT_READ;
1300 break;
1301 case O_WRONLY:
1302 prot = PROT_WRITE;
1303 break;
1304 default:
1305 prot = PROT_READ | PROT_WRITE;
1306 }
1307
1308 enterMutex();
1309 lseek(fd, pos + len, SEEK_SET);
1310 fcb.address = (caddr_t)mmap(NULL, len, prot, MAP_SHARED, fd, pos);
1311 fcb.len = len;
1312 fcb.pos = pos;
1313 leaveMutex();
1314 if((caddr_t)(fcb.address) == (caddr_t)(MAP_FAILED)) {
1315 close(fd);
1316 fd = -1;
1317 error(errMapFailed);
1318 }
1319}
1320
1321MappedFile::~MappedFile()
1322{
1323 unlock();
1324 final();
1325}
1326
1327void MappedFile::sync(void)
1328{
1329 msync(fcb.address, fcb.len, MS_SYNC);
1330}
1331
1332void MappedFile::release(caddr_t address, size_t len)
1333{
1334 enterMutex();
1335 if(address)
1336 fcb.address = address;
1337
1338 if(len)
1339 fcb.len = len;
1340
1341 if(fcb.locked)
1342 unlock();
1343
1344 munmap(fcb.address, fcb.len);
1345 leaveMutex();
1346}
1347
1348caddr_t MappedFile::fetch(off_t pos, size_t len)
1349{
1350 enterMutex();
1351 unlock();
1352 fcb.len = len;
1353 fcb.pos = pos;
1354 lseek(fd, fcb.pos + len, SEEK_SET);
1355 fcb.address = (caddr_t)mmap(NULL, len, prot, MAP_SHARED, fd, pos);
1356 leaveMutex();
1357 return fcb.address;
1358}
1359
1360bool MappedFile::lock(void)
1361{
1362 unlock();
1363 if(!mlock(fcb.address, fcb.len))
1364 fcb.locked = true;
1365 return fcb.locked;
1366}
1367
1368void MappedFile::unlock(void)
1369{
1370 if(!fcb.address)
1371 fcb.locked = false;
1372
1373 if(!fcb.locked)
1374 return;
1375
1376 munlock(fcb.address, fcb.len);
1377 fcb.locked = false;
1378}
1379
1380void MappedFile::update(size_t offset, size_t len)
1381{
1382 int mode = MS_ASYNC;
1383 caddr_t address;
1384
1385 if(flags.immediate)
1386 mode = MS_SYNC;
1387
1388 enterMutex();
1389 address = fcb.address;
1390 address += offset;
1391 if(!len)
1392 len = fcb.len;
1393 leaveMutex();
1394 msync(address, len, mode);
1395}
1396
1397void MappedFile::update(caddr_t address, size_t len)
1398{
1399 int mode = MS_ASYNC;
1400 if(flags.immediate)
1401 mode = MS_SYNC;
1402
1403 msync(address, len, mode);
1404}
1405#endif
1406#endif // ndef WIN32
1407
1408#ifdef WIN32
1409#ifndef SECS_BETWEEN_EPOCHS
1410#define SECS_BETWEEN_EPOCHS 11644473600LL
1411#endif
1412
1413#ifndef SECS_TO_100NS
1414#define SECS_TO_100NS 10000000LL
1415#endif
1416#endif
1417
1418time_t lastAccessed(const char *path)
1419{
1420#ifdef WIN32
1421 __int64 ts;
1422 WIN32_FILE_ATTRIBUTE_DATA ino;
1423
1424 if(!GetFileAttributesEx(path, GetFileExInfoStandard, &ino))
1425 return 0;
1426
1427 ts = ((__int64)ino.ftLastAccessTime.dwHighDateTime << 32) +
1428 ino.ftLastAccessTime.dwLowDateTime;
1429
1430 ts -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
1431 ts /= SECS_TO_100NS;
1432
1433 return(time_t)ts;
1434#else
1435 struct stat ino;
1436
1437 if(stat(path, &ino))
1438 return 0;
1439
1440 return ino.st_atime;
1441#endif
1442}
1443
1444time_t lastModified(const char *path)
1445{
1446#ifdef WIN32
1447 __int64 ts;
1448 WIN32_FILE_ATTRIBUTE_DATA ino;
1449
1450 if(!GetFileAttributesEx(path, GetFileExInfoStandard, &ino))
1451 return 0;
1452
1453 ts = ((__int64)ino.ftLastWriteTime.dwHighDateTime << 32) +
1454 ino.ftLastWriteTime.dwLowDateTime;
1455
1456 ts -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
1457 ts /= SECS_TO_100NS;
1458
1459 return(time_t)ts;
1460#else
1461 struct stat ino;
1462
1463 if(stat(path, &ino))
1464 return 0;
1465
1466 return ino.st_mtime;
1467#endif
1468}
1469
1470bool isDir(const char *path)
1471{
1472#ifdef WIN32
1473 DWORD attr = GetFileAttributes(path);
1474 if(attr == (DWORD)~0l)
1475 return false;
1476
1477 if(attr & FILE_ATTRIBUTE_DIRECTORY)
1478 return true;
1479
1480 return false;
1481
1482#else
1483 struct stat ino;
1484
1485 if(stat(path, &ino))
1486 return false;
1487
1488 if(S_ISDIR(ino.st_mode))
1489 return true;
1490
1491 return false;
1492#endif // WIN32
1493}
1494
1495bool isFile(const char *path)
1496{
1497#ifdef WIN32
1498 DWORD attr = GetFileAttributes(path);
1499 if(attr == (DWORD)~0l)
1500 return false;
1501
1502 if(attr & FILE_ATTRIBUTE_DIRECTORY)
1503 return false;
1504
1505 return true;
1506
1507#else
1508 struct stat ino;
1509
1510 if(stat(path, &ino))
1511 return false;
1512
1513 if(S_ISREG(ino.st_mode))
1514 return true;
1515
1516 return false;
1517#endif // WIN32
1518}
1519
1520#ifndef WIN32
1521// the Win32 version is given in line in the header
1522bool isDevice(const char *path)
1523{
1524 struct stat ino;
1525
1526 if(stat(path, &ino))
1527 return false;
1528
1529 if(S_ISCHR(ino.st_mode))
1530 return true;
1531
1532 return false;
1533}
1534#endif
1535
1536bool canAccess(const char *path)
1537{
1538#ifdef WIN32
1539 DWORD attr = GetFileAttributes(path);
1540 if(attr == (DWORD)~0l)
1541 return false;
1542
1543 if(attr & FILE_ATTRIBUTE_SYSTEM)
1544 return false;
1545
1546 if(attr & FILE_ATTRIBUTE_HIDDEN)
1547 return false;
1548
1549 return true;
1550
1551#else
1552 if(!access(path, R_OK))
1553 return true;
1554
1555 return false;
1556
1557#endif
1558}
1559
1560bool canModify(const char *path)
1561{
1562#ifdef WIN32
1563 DWORD attr = GetFileAttributes(path);
1564
1565 if(!canAccess(path))
1566 return false;
1567
1568 if(attr & FILE_ATTRIBUTE_READONLY)
1569 return false;
1570
1571 if(attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_NORMAL))
1572 return true;
1573
1574 return false;
1575
1576#else
1577 if(!access(path, W_OK | R_OK))
1578 return true;
1579
1580 return false;
1581#endif
1582}
1583
1584#ifdef WIN32
1585
1586const char *File::getExtension(const char *path)
1587{
1588 const char *cp = strrchr(path, '\\');
1589 if(!cp)
1590 cp = strrchr(path, '/');
1591 if(!cp)
1592 cp = strrchr(path, ':');
1593
1594
1595 if(cp)
1596 ++cp;
1597 else
1598 cp = path;
1599
1600 if(*cp == '.')
1601 return "";
1602
1603 cp = strrchr(cp, '.');
1604
1605 if(!cp)
1606 cp = "";
1607
1608 return cp;
1609}
1610
1611const char *File::getFilename(const char *path)
1612{
1613 const char *cp = strrchr(path, '\\');
1614 if(cp)
1615 return ++cp;
1616
1617 cp = strrchr(path, '/');
1618 if(cp)
1619 return ++cp;
1620
1621 cp = strrchr(path, ':');
1622 if(cp)
1623 ++cp;
1624
1625 return path;
1626}
1627
1628char *File::getFilename(const char *path, char *buffer, size_t size)
1629{
1630 const char *cp = strrchr(path, '\\');
1631 if(!cp)
1632 cp = strrchr(path, '/');
1633 if(!cp)
1634 cp = strrchr(path, ':');
1635
1636 if(cp)
1637 snprintf(buffer, size, "%s", ++cp);
1638 else
1639 snprintf(buffer, size, "%s", path);
1640
1641 return buffer;
1642}
1643
1644char *File::getDirname(const char *path, char *buffer, size_t size)
1645{
1646 size_t len;
1647 const char *cp = strrchr(path, '\\');
1648
1649 snprintf(buffer, size, "%s", path);
1650
1651 if(!cp)
1652 cp = strrchr(path, '/');
1653 if(!cp)
1654 cp = strrchr(path, ':');
1655
1656 if(!cp)
1657 return buffer;
1658
1659 if(cp)
1660 len = cp - path;
1661
1662 if(len >= size)
1663 len = size - 1;
1664 buffer[len] = 0;
1665 return buffer;
1666}
1667
1668#else
1669
1670const char *File::getExtension(const char *path)
1671{
1672 const char *cp = strrchr(path, '/');
1673 if(cp)
1674 ++cp;
1675 else
1676 cp = path;
1677
1678 if(*cp == '.')
1679 return "";
1680
1681 cp = strrchr(cp, '.');
1682 if(cp)
1683 return cp;
1684 return "";
1685}
1686
1687char *File::getDirname(const char *path, char *buffer, size_t size)
1688{
1689 unsigned len;
1690 const char *cp = strrchr(path, '/');
1691
1692 snprintf(buffer, size, "%s", path);
1693
1694 if(!cp)
1695 return buffer;
1696
1697 if(cp)
1698 len = cp - path;
1699
1700 if(len >= size)
1701 len = size - 1;
1702 buffer[len] = 0;
1703 return buffer;
1704}
1705
1706const char *File::getFilename(const char *path)
1707{
1708 const char *cp = strrchr(path, '/');
1709
1710 if(cp)
1711 return ++cp;
1712
1713 return path;
1714}
1715
1716char *File::getFilename(const char *path, char *buffer, size_t size)
1717{
1718 const char *cp = strrchr(path, '/');
1719
1720 if(cp)
1721 snprintf(buffer, size, "%s", ++cp);
1722 else
1723 snprintf(buffer, size, "%s", path);
1724
1725 return buffer;
1726}
1727
1728#endif
1729
1730#ifdef HAVE_REALPATH
1731
1732char *File::getRealpath(const char *path, char *buffer, size_t size)
1733{
1734 char temp[PATH_MAX];
1735 setString(buffer, size, ".");
1736 if(!realpath(path, temp))
1737 return NULL;
1738 if(strlen(temp) >= size)
1739 return NULL;
1740 setString(buffer, size, temp);
1741 return buffer;
1742}
1743
1744#else
1745
1746#ifdef WIN323
1747static char *getFile(char *path)
1748{
1749 char *cp = strchr(path, '\\');
1750 if(!cp)
1751 cp = strchr(path, '/');
1752 if(!cp)
1753 cp = strchr(path, ':');
1754 return cp;
1755}
1756#else
1757static char *getFile(char *path)
1758{
1759 return strchr(path, '/');
1760}
1761#endif
1762
1763char *File::getRealpath(const char *path, char *buffer, size_t size)
1764{
1765 if(size > PATH_MAX)
1766 size = PATH_MAX;
1767
1768 unsigned symlinks = 0;
1769#if !defined(DYNAMCIC_LOCAL_ARRAYS)
1770 char left[PATH_MAX];
1771#else
1772 char left[size];
1773#endif
1774 size_t left_len, buffer_len;
1775
1776#ifdef WIN32
1777 if(path[1] == ':')
1778#else
1779 if(path[0] == '/')
1780#endif
1781 {
1782 buffer[0] = '/';
1783 buffer[1] = 0;
1784 if(!path[1])
1785 return buffer;
1786
1787 buffer_len = 1;
1788 snprintf(left, size, "%s", path + 1);
1789 left_len = strlen(left);
1790 }
1791 else {
1792 if(!Dir::getPrefix(buffer, size)) {
1793 snprintf(buffer, size, "%s", ".");
1794 return NULL;
1795 }
1796 buffer_len = strlen(buffer);
1797 snprintf(left, size, "%s", path);
1798 left_len = strlen(left);
1799 }
1800
1801 if(left_len >= size || buffer_len >= size)
1802 return NULL;
1803
1804 while(left_len > 0) {
1805#ifdef HAVE_LSTAT
1806 struct stat ino;
1807#endif
1808#if !defined(DYNAMIC_LOCAL_ARRAYS)
1809 char next_token[PATH_MAX];
1810#else
1811 char next_token[size];
1812#endif
1813 char *p;
1814 const char *s = (p = getFile(left)) ? p : left + left_len;
1815
1816 memmove(next_token, left, s - left);
1817 left_len -= s - left;
1818 if(p != NULL)
1819 memmove(left, s + 1, left_len + 1);
1820
1821 next_token[s - left] = 0;
1822 if(buffer[buffer_len - 1] != '/') {
1823 if(buffer_len +1 >= size)
1824 return NULL;
1825
1826 buffer[buffer_len++] = '/';
1827 buffer[buffer_len] = 0;
1828 }
1829 if(!next_token[0])
1830 continue;
1831 else if(!strcmp(next_token, "."))
1832 continue;
1833 else if(!strcmp(next_token, "..")) {
1834 if(buffer_len > 1) {
1835 char *q;
1836 buffer[buffer_len - 1] = 0;
1837#ifdef WIN32
1838 q = strrchr(buffer, '\\');
1839 if(!q)
1840 q = strrchr(buffer, '/');
1841 if(!q)
1842 q = strchr(buffer, ':');
1843#else
1844 q = strrchr(buffer, '/');
1845#endif
1846 *q = 0;
1847 buffer_len = q - buffer;
1848 }
1849 continue;
1850 }
1851 snprintf(next_token, size, "%s", buffer);
1852 buffer_len = strlen(buffer);
1853 if(buffer_len >= size)
1854 return NULL;
1855
1856#ifndef HAVE_LSTAT
1857 if(!isFile(buffer) && !isDir(buffer))
1858 return buffer;
1859
1860 continue;
1861#else
1862 if(lstat(buffer, &ino) < 0) {
1863 if(errno == ENOENT && !p)
1864 return buffer;
1865
1866 return NULL;
1867 }
1868
1869 if((ino.st_mode & S_IFLNK) == S_IFLNK) {
1870 char symlink[size];
1871 int slen;
1872
1873 if (symlinks++ > MAXSYMLINKS)
1874 return NULL;
1875
1876 slen = readlink(buffer, symlink, size);
1877
1878 if (slen < 0)
1879 return NULL;
1880 symlink[slen] = 0;
1881
1882 if (symlink[0] == '/') {
1883 buffer[1] = 0;
1884 buffer_len = 1;
1885 } else if (buffer_len > 1) {
1886 char *q;
1887
1888 buffer[buffer_len - 1] = 0;
1889 q = strrchr(buffer, '/');
1890 *q = 0;
1891 buffer_len = q - buffer;
1892 }
1893 if (symlink[slen - 1] != '/' && p) {
1894 if (slen >= size)
1895 return NULL;
1896
1897 symlink[slen] = '/';
1898 symlink[slen + 1] = 0;
1899 }
1900 if(p) {
1901 snprintf(symlink, size, "%s", left);
1902 left_len = strlen(symlink);
1903 }
1904 if(left_len >= size)
1905 return NULL;
1906 snprintf(left, size, "%s", symlink);
1907 left_len = strlen(left);
1908 }
1909#endif
1910
1911 }
1912
1913#ifdef WIN32
1914 if(buffer_len > 1 && buffer[buffer_len - 1] == '\\')
1915 buffer[buffer_len - 1] = 0;
1916 else if(buffer_len > 1 && buffer[buffer_len - 1] == '/')
1917 buffer[buffer_len - 1] = 0;
1918#else
1919 if(buffer_len > 1 && buffer[buffer_len - 1] == '/')
1920 buffer[buffer_len - 1] = 0;
1921#endif
1922 return buffer;
1923}
1924
1925#endif
1926
1927#ifdef CCXX_NAMESPACES
1928}
1929#endif
1930
1931/** EMACS **
1932 * Local variables:
1933 * mode: c++
1934 * c-basic-offset: 4
1935 * End:
1936 */