1 /*
2 * pchannel.cxx
3 *
4 * Operating System utilities.
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 * $Revision: 26933 $
30 * $Author: rjongbloed $
31 * $Date: 2012-02-02 21:17:20 -0600 (Thu, 02 Feb 2012) $
32 */
33
34 #include <ptlib.h>
35
36 #include <ctype.h>
37
38
39 #define new PNEW
40
41
42 ///////////////////////////////////////////////////////////////////////////////
43 // PChannel
44
PChannelStreamBuffer(PChannel * chan)45 PChannelStreamBuffer::PChannelStreamBuffer(PChannel * chan)
46 : channel(PAssertNULL(chan))
47 {
48 }
49
50
SetBufferSize(PINDEX newSize)51 PBoolean PChannelStreamBuffer::SetBufferSize(PINDEX newSize)
52 {
53 return input.SetSize(newSize) && output.SetSize(newSize);
54 }
55
56
overflow(int_type c)57 streambuf::int_type PChannelStreamBuffer::overflow(int_type c)
58 {
59 if (pbase() == NULL) {
60 char * p = output.GetPointer(1024);
61 setp(p, p+output.GetSize());
62 }
63
64 int bufSize = pptr() - pbase();
65 if (bufSize > 0) {
66 setp(pbase(), epptr());
67 if (!channel->Write(pbase(), bufSize))
68 return EOF;
69 }
70
71 if (c != EOF) {
72 *pptr() = (char)c;
73 pbump(1);
74 }
75
76 return 0;
77 }
78
79
underflow()80 streambuf::int_type PChannelStreamBuffer::underflow()
81 {
82 if (eback() == NULL) {
83 char * p = input.GetPointer(1024);
84 char * e = p+input.GetSize();
85 setg(p, e, e);
86 }
87
88 if (gptr() != egptr())
89 return (BYTE)*gptr();
90
91 if (!channel->Read(eback(), egptr() - eback()) ||
92 channel->GetErrorCode() != PChannel::NoError)
93 return EOF;
94
95 PINDEX count = channel->GetLastReadCount();
96 char * p = egptr() - count;
97 memmove(p, eback(), count);
98 setg(eback(), p, egptr());
99 return (BYTE)*p;
100 }
101
102
sync()103 int PChannelStreamBuffer::sync()
104 {
105 int inAvail = egptr() - gptr();
106 if (inAvail > 0) {
107 setg(eback(), egptr(), egptr());
108 if (PIsDescendant(channel, PFile))
109 ((PFile *)channel)->SetPosition(-inAvail, PFile::Current);
110 }
111
112 if (pptr() > pbase())
113 return overflow();
114
115 return 0;
116 }
117
118
seekoff(off_type off,ios_base::seekdir dir,ios_base::openmode)119 PChannelStreamBuffer::pos_type PChannelStreamBuffer::seekoff(off_type off, ios_base::seekdir dir, ios_base::openmode)
120 {
121 sync();
122 if (PIsDescendant(channel, PFile)) {
123 PFile * file = (PFile *)channel;
124 file->SetPosition((off_t)off, (PFile::FilePositionOrigin)dir);
125 return file->GetPosition();
126 }
127
128 // If we have an input stream and the buffer is empty then force a read so
129 // we can seek ahead.
130 if (egptr() == gptr()) {
131 int c = underflow();
132 if (c == EOF)
133 return EOF;
134 }
135
136 while (off-- > 0) {
137 if (sbumpc() == EOF)
138 return EOF;
139 }
140
141 return egptr() - gptr();
142 }
143
144
seekpos(pos_type pos,ios_base::openmode mode)145 PChannelStreamBuffer::pos_type PChannelStreamBuffer::seekpos(pos_type pos, ios_base::openmode mode)
146 {
147 return seekoff(pos, ios_base::beg, mode);
148 }
149
150
151 #ifdef _MSC_VER
152 #pragma warning(disable:4355)
153 #endif
154
PChannel()155 PChannel::PChannel()
156 : iostream(new PChannelStreamBuffer(this)),
157 readTimeout(PMaxTimeInterval), writeTimeout(PMaxTimeInterval)
158 {
159 os_handle = -1;
160 memset(lastErrorCode, 0, sizeof(lastErrorCode));
161 memset(lastErrorNumber, 0, sizeof(lastErrorNumber));
162 lastReadCount = lastWriteCount = 0;
163 Construct();
164 }
165
166 #ifdef _MSC_VER
167 #pragma warning(default:4355)
168 #endif
169
170
~PChannel()171 PChannel::~PChannel()
172 {
173 flush();
174 Close();
175 delete (PChannelStreamBuffer *)rdbuf();
176 #ifndef _WIN32
177 init(NULL);
178 #endif
179 }
180
Compare(const PObject & obj) const181 PObject::Comparison PChannel::Compare(const PObject & obj) const
182 {
183 PAssert(PIsDescendant(&obj, PChannel), PInvalidCast);
184 int h1 = GetHandle();
185 int h2 = ((const PChannel&)obj).GetHandle();
186 if (h1 < h2)
187 return LessThan;
188 if (h1 > h2)
189 return GreaterThan;
190 return EqualTo;
191 }
192
193
HashFunction() const194 PINDEX PChannel::HashFunction() const
195 {
196 return GetHandle()%97;
197 }
198
199
IsOpen() const200 PBoolean PChannel::IsOpen() const
201 {
202 return os_handle != -1;
203 }
204
GetLastReadCount() const205 PINDEX PChannel::GetLastReadCount() const
206 {
207 return lastReadCount;
208 }
209
GetLastWriteCount() const210 PINDEX PChannel::GetLastWriteCount() const
211 {
212 return lastWriteCount;
213 }
214
ReadChar()215 int PChannel::ReadChar()
216 {
217 BYTE c;
218 PBoolean retVal = Read(&c, 1);
219 return (retVal && lastReadCount == 1) ? c : -1;
220 }
221
222
ReadCharWithTimeout(PTimeInterval & timeout)223 int PChannel::ReadCharWithTimeout(PTimeInterval & timeout)
224 {
225 SetReadTimeout(timeout);
226 PTimeInterval startTick = PTimer::Tick();
227 int c;
228 if ((c = ReadChar()) < 0) // Timeout or aborted
229 return -1;
230 timeout -= PTimer::Tick() - startTick;
231 return c;
232 }
233
234
ReadBlock(void * buf,PINDEX len)235 PBoolean PChannel::ReadBlock(void * buf, PINDEX len)
236 {
237 char * ptr = (char *)buf;
238 PINDEX numRead = 0;
239
240 while (numRead < len && Read(ptr+numRead, len - numRead))
241 numRead += lastReadCount;
242
243 lastReadCount = numRead;
244
245 return lastReadCount == len;
246 }
247
248
ReadString(PINDEX len)249 PString PChannel::ReadString(PINDEX len)
250 {
251 PString str;
252
253 if (len == P_MAX_INDEX) {
254 PINDEX l = 0;
255 for (;;) {
256 char * p = l + str.GetPointer(l+1000+1);
257 if (!Read(p, 1000))
258 break;
259 l += lastReadCount;
260 }
261 str.SetSize(l+1);
262
263 /*Need to put in a null at the end to allow for MSDOS/Win32 text files
264 which returns fewer bytes than actually read as it shrinks the data into
265 the removed carriage returns, so it actually changes the buffer beyond
266 what it indicated. */
267 str[l] = '\0';
268 }
269 else {
270 if (!ReadBlock(str.GetPointer(len+1), len))
271 return PString::Empty();
272 }
273
274 return str;
275 }
276
277
WriteString(const PString & str)278 PBoolean PChannel::WriteString(const PString & str)
279 {
280 PINDEX len = str.GetLength();
281 PINDEX written = 0;
282 while (written < len) {
283 if (!Write((const char *)str + written, len - written)) {
284 lastWriteCount += written;
285 return PFalse;
286 }
287 written += lastWriteCount;
288 }
289 lastWriteCount = written;
290 return PTrue;
291 }
292
293
ReadAsync(void * buf,PINDEX len)294 PBoolean PChannel::ReadAsync(void * buf, PINDEX len)
295 {
296 PBoolean retVal = Read(buf, len);
297 OnReadComplete(buf, lastReadCount);
298 return retVal;
299 }
300
301
OnReadComplete(void *,PINDEX)302 void PChannel::OnReadComplete(void *, PINDEX)
303 {
304 }
305
306
WriteChar(int c)307 PBoolean PChannel::WriteChar(int c)
308 {
309 PAssert(c >= 0 && c < 256, PInvalidParameter);
310 char buf = (char)c;
311 return Write(&buf, 1);
312 }
313
314
WriteAsync(const void * buf,PINDEX len)315 PBoolean PChannel::WriteAsync(const void * buf, PINDEX len)
316 {
317 PBoolean retVal = Write(buf, len);
318 OnWriteComplete(buf, lastWriteCount);
319 return retVal;
320 }
321
322
OnWriteComplete(const void *,PINDEX)323 void PChannel::OnWriteComplete(const void *, PINDEX)
324 {
325 }
326
327
SetBufferSize(PINDEX newSize)328 PBoolean PChannel::SetBufferSize(PINDEX newSize)
329 {
330 return ((PChannelStreamBuffer *)rdbuf())->SetBufferSize(newSize);
331 }
332
333
334 enum {
335 NextCharEndOfString = -1,
336 NextCharDelay = -2,
337 NextCharSend = -3,
338 NextCharWait = -4
339 };
340
341
HexDigit(char c)342 static int HexDigit(char c)
343 {
344 if (!isxdigit(c))
345 return 0;
346
347 int hex = c - '0';
348 if (hex < 10)
349 return hex;
350
351 hex -= 'A' - '9' - 1;
352 if (hex < 16)
353 return hex;
354
355 return hex - ('a' - 'A');
356 }
357
358
GetNextChar(const PString & command,PINDEX & pos,PTimeInterval * time=NULL)359 static int GetNextChar(const PString & command,
360 PINDEX & pos, PTimeInterval * time = NULL)
361 {
362 int temp;
363
364 if (command[pos] == '\0')
365 return NextCharEndOfString;
366
367 if (command[pos] != '\\')
368 return command[pos++];
369
370 switch (command[++pos]) {
371 case '\0' :
372 return NextCharEndOfString;
373
374 case 'a' : // alert (ascii value 7)
375 pos++;
376 return 7;
377
378 case 'b' : // backspace (ascii value 8)
379 pos++;
380 return 8;
381
382 case 'f' : // formfeed (ascii value 12)
383 pos++;
384 return 12;
385
386 case 'n' : // newline (ascii value 10)
387 pos++;
388 return 10;
389
390 case 'r' : // return (ascii value 13)
391 pos++;
392 return 13;
393
394 case 't' : // horizontal tab (ascii value 9)
395 pos++;
396 return 9;
397
398 case 'v' : // vertical tab (ascii value 11)
399 pos++;
400 return 11;
401
402 case 'x' : // followed by hh where nn is hex number (ascii value 0xhh)
403 if (isxdigit(command[++pos])) {
404 temp = HexDigit(command[pos++]);
405 if (isxdigit(command[pos]))
406 temp += HexDigit(command[pos++]);
407 return temp;
408 }
409 return command[pos];
410
411 case 's' :
412 pos++;
413 return NextCharSend;
414
415 case 'd' : // ns delay for n seconds/milliseconds
416 case 'w' :
417 temp = command[pos] == 'd' ? NextCharDelay : NextCharWait;
418 long milliseconds = 0;
419 while (isdigit(command[++pos]))
420 milliseconds = milliseconds*10 + command[pos] - '0';
421 if (milliseconds <= 0)
422 milliseconds = 1;
423 if (command[pos] == 'm')
424 pos++;
425 else {
426 milliseconds *= 1000;
427 if (command[pos] == 's')
428 pos++;
429 }
430 if (time != NULL)
431 *time = milliseconds;
432 return temp;
433 }
434
435 if (command[pos] < '0' || command[pos] > '7')
436 return command[pos++];
437
438 // octal number
439 temp = command[pos++] - '0';
440 if (command[pos] < '0' || command[pos] > '7')
441 return temp;
442
443 temp += command[pos++] - '0';
444 if (command[pos] < '0' || command[pos] > '7')
445 return temp;
446
447 temp += command[pos++] - '0';
448 return temp;
449 }
450
451
ReceiveCommandString(int nextChar,const PString & reply,PINDEX & pos,PINDEX start)452 PBoolean PChannel::ReceiveCommandString(int nextChar,
453 const PString & reply, PINDEX & pos, PINDEX start)
454 {
455 if (nextChar != GetNextChar(reply, pos)) {
456 pos = start;
457 return PFalse;
458 }
459
460 PINDEX dummyPos = pos;
461 return GetNextChar(reply, dummyPos) < 0;
462 }
463
464
SendCommandString(const PString & command)465 PBoolean PChannel::SendCommandString(const PString & command)
466 {
467 abortCommandString = PFalse;
468
469 int nextChar;
470 PINDEX sendPosition = 0;
471 PTimeInterval timeout;
472 SetWriteTimeout(10000);
473
474 while (!abortCommandString) { // not aborted
475 nextChar = GetNextChar(command, sendPosition, &timeout);
476 switch (nextChar) {
477 default :
478 if (!WriteChar(nextChar))
479 return PFalse;
480 break;
481
482 case NextCharEndOfString :
483 return PTrue; // Success!!
484
485 case NextCharSend :
486 break;
487
488 case NextCharDelay : // Delay in send
489 PThread::Sleep(timeout);
490 break;
491
492 case NextCharWait : // Wait for reply
493 PINDEX receivePosition = sendPosition;
494 if (GetNextChar(command, receivePosition) < 0) {
495 SetReadTimeout(timeout);
496 while (ReadChar() >= 0)
497 if (abortCommandString) // aborted
498 return PFalse;
499 }
500 else {
501 receivePosition = sendPosition;
502 do {
503 if (abortCommandString) // aborted
504 return PFalse;
505 if ((nextChar = ReadCharWithTimeout(timeout)) < 0)
506 return PFalse;
507 } while (!ReceiveCommandString(nextChar,
508 command, receivePosition, sendPosition));
509 // nextChar = GetNextChar(command, receivePosition);
510 sendPosition = receivePosition;
511 }
512 }
513 }
514
515 return PFalse;
516 }
517
518
Shutdown(ShutdownValue)519 PBoolean PChannel::Shutdown(ShutdownValue)
520 {
521 return PFalse;
522 }
523
524
SetLocalEcho(bool)525 bool PChannel::SetLocalEcho(bool /*localEcho*/)
526 {
527 return IsOpen();
528 }
529
FlowControl(const void *)530 bool PChannel::FlowControl(const void * /*flowData*/)
531 {
532 return false;
533 }
534
GetBaseReadChannel() const535 PChannel * PChannel::GetBaseReadChannel() const
536 {
537 return (PChannel *)this;
538 }
539
540
GetBaseWriteChannel() const541 PChannel * PChannel::GetBaseWriteChannel() const
542 {
543 return (PChannel *)this;
544 }
545
546
GetErrorText(ErrorGroup group) const547 PString PChannel::GetErrorText(ErrorGroup group) const
548 {
549 return GetErrorText(lastErrorCode[group], lastErrorNumber[group]);
550 }
551
552
ConvertOSError(int status,ErrorGroup group)553 PBoolean PChannel::ConvertOSError(int status, ErrorGroup group)
554 {
555 Errors lastError;
556 int osError;
557 PBoolean ok = ConvertOSError(status, lastError, osError);
558 SetErrorValues(lastError, osError, group);
559 return ok;
560 }
561
562
SetErrorValues(Errors errorCode,int errorNum,ErrorGroup group)563 PBoolean PChannel::SetErrorValues(Errors errorCode, int errorNum, ErrorGroup group)
564 {
565 lastErrorCode[NumErrorGroups] = lastErrorCode[group] = errorCode;
566 lastErrorNumber[NumErrorGroups] = lastErrorNumber[group] = errorNum;
567 return errorCode == NoError;
568 }
569
570 #ifndef P_HAS_RECVMSG
571
Read(const VectorOfSlice & slices)572 PBoolean PChannel::Read(const VectorOfSlice & slices)
573 {
574 PINDEX length = 0;
575
576 VectorOfSlice::const_iterator r;
577 for (r = slices.begin(); r != slices.end(); ++r) {
578 PBoolean stat = Read(r->iov_base, r->iov_len);
579 length += lastReadCount;
580 lastReadCount = length;
581 if (!stat)
582 return PFalse;
583 }
584
585 return PTrue;
586 }
587
Write(const VectorOfSlice & slices)588 PBoolean PChannel::Write(const VectorOfSlice & slices)
589 {
590 PINDEX length = 0;
591
592 VectorOfSlice::const_iterator r;
593 for (r = slices.begin(); r != slices.end(); ++r) {
594 PBoolean stat = Write(r->iov_base, r->iov_len);
595 length += lastWriteCount;
596 lastWriteCount = length;
597 if (!stat)
598 return PFalse;
599 }
600
601 return PTrue;
602 }
603
604 #endif // P_HAS_RECVMSG
605
606 ///////////////////////////////////////////////////////////////////////////////
607 // PIndirectChannel
608
PIndirectChannel()609 PIndirectChannel::PIndirectChannel()
610 {
611 readChannel = writeChannel = NULL;
612 writeAutoDelete = readAutoDelete = PFalse;
613 }
614
615
Compare(const PObject & obj) const616 PObject::Comparison PIndirectChannel::Compare(const PObject & obj) const
617 {
618 PAssert(PIsDescendant(&obj, PIndirectChannel), PInvalidCast);
619 const PIndirectChannel & other = (const PIndirectChannel &)obj;
620 return readChannel == other.readChannel &&
621 writeChannel == other.writeChannel ? EqualTo : GreaterThan;
622 }
623
624
GetName() const625 PString PIndirectChannel::GetName() const
626 {
627 PReadWaitAndSignal mutex(channelPointerMutex);
628
629 if (readChannel != NULL && readChannel == writeChannel)
630 return readChannel->GetName();
631
632 PStringStream name;
633
634 name << "R<";
635 if (readChannel != NULL)
636 name << readChannel->GetName();
637 name << "> T<";
638 if (writeChannel != NULL)
639 name << writeChannel->GetName();
640 name << '>';
641
642 return name;
643 }
644
645
Close()646 PBoolean PIndirectChannel::Close()
647 {
648 PBoolean retval = PTrue;
649
650 flush();
651
652 channelPointerMutex.StartRead();
653
654 if (readChannel != NULL)
655 retval = readChannel->Close();
656
657 if (readChannel != writeChannel && writeChannel != NULL)
658 retval = writeChannel->Close() && retval;
659
660 channelPointerMutex.EndRead();
661
662 channelPointerMutex.StartWrite();
663
664 PChannel * r = readChannel;
665 PChannel * w = writeChannel;
666
667 readChannel = NULL;
668 writeChannel = NULL;
669
670 if (readAutoDelete)
671 delete r;
672
673 if (r != w && writeAutoDelete)
674 delete w;
675
676 channelPointerMutex.EndWrite();
677
678 return retval;
679 }
680
681
IsOpen() const682 PBoolean PIndirectChannel::IsOpen() const
683 {
684 PReadWaitAndSignal mutex(channelPointerMutex);
685
686 if (readChannel != NULL && readChannel == writeChannel)
687 return readChannel->IsOpen();
688
689 PBoolean returnValue = readChannel != NULL ? readChannel->IsOpen() : PFalse;
690
691 if (writeChannel != NULL)
692 returnValue = writeChannel->IsOpen() || returnValue;
693
694 return returnValue;
695 }
696
697
Read(void * buf,PINDEX len)698 PBoolean PIndirectChannel::Read(void * buf, PINDEX len)
699 {
700 flush();
701
702 PReadWaitAndSignal mutex(channelPointerMutex);
703
704 if (readChannel == NULL) {
705 SetErrorValues(NotOpen, EBADF, LastReadError);
706 return PFalse;
707 }
708
709 readChannel->SetReadTimeout(readTimeout);
710 PBoolean returnValue = readChannel->Read(buf, len);
711
712 SetErrorValues(readChannel->GetErrorCode(LastReadError),
713 readChannel->GetErrorNumber(LastReadError),
714 LastReadError);
715 lastReadCount = readChannel->GetLastReadCount();
716
717 return returnValue;
718 }
719
720
Write(const void * buf,PINDEX len)721 PBoolean PIndirectChannel::Write(const void * buf, PINDEX len)
722 {
723 flush();
724
725 PReadWaitAndSignal mutex(channelPointerMutex);
726
727 if (writeChannel == NULL) {
728 SetErrorValues(NotOpen, EBADF, LastWriteError);
729 return PFalse;
730 }
731
732 writeChannel->SetWriteTimeout(writeTimeout);
733 PBoolean returnValue = writeChannel->Write(buf, len);
734
735 SetErrorValues(writeChannel->GetErrorCode(LastWriteError),
736 writeChannel->GetErrorNumber(LastWriteError),
737 LastWriteError);
738
739 lastWriteCount = writeChannel->GetLastWriteCount();
740
741 return returnValue;
742 }
743
744
Shutdown(ShutdownValue value)745 PBoolean PIndirectChannel::Shutdown(ShutdownValue value)
746 {
747 PReadWaitAndSignal mutex(channelPointerMutex);
748
749 if (readChannel != NULL && readChannel == writeChannel)
750 return readChannel->Shutdown(value);
751
752 PBoolean returnValue = readChannel != NULL ? readChannel->Shutdown(value) : PFalse;
753
754 if (writeChannel != NULL)
755 returnValue = writeChannel->Shutdown(value) || returnValue;
756
757 return returnValue;
758 }
759
760
SetLocalEcho(bool localEcho)761 bool PIndirectChannel::SetLocalEcho(bool localEcho)
762 {
763 PReadWaitAndSignal mutex(channelPointerMutex);
764 return readChannel != NULL && readChannel->SetLocalEcho(localEcho);
765 }
766
767
GetErrorText(ErrorGroup group) const768 PString PIndirectChannel::GetErrorText(ErrorGroup group) const
769 {
770 if (readChannel != NULL)
771 return readChannel->GetErrorText(group);
772
773 if (writeChannel != NULL)
774 return writeChannel->GetErrorText(group);
775
776 return PChannel::GetErrorText(group);
777 }
778
779
Open(PChannel & channel)780 PBoolean PIndirectChannel::Open(PChannel & channel)
781 {
782 return Open(&channel, (PBoolean)PFalse);
783 }
784
785
Open(PChannel * channel,PBoolean autoDelete)786 PBoolean PIndirectChannel::Open(PChannel * channel, PBoolean autoDelete)
787 {
788 return Open(channel, channel, autoDelete, autoDelete);
789 }
790
791
Open(PChannel * readChan,PChannel * writeChan,PBoolean autoDeleteRead,PBoolean autoDeleteWrite)792 PBoolean PIndirectChannel::Open(PChannel * readChan,
793 PChannel * writeChan,
794 PBoolean autoDeleteRead,
795 PBoolean autoDeleteWrite)
796 {
797 flush();
798
799 channelPointerMutex.StartWrite();
800
801 if (readChannel != NULL)
802 readChannel->Close();
803
804 if (readChannel != writeChannel && writeChannel != NULL)
805 writeChannel->Close();
806
807 if (readAutoDelete)
808 delete readChannel;
809
810 if (readChannel != writeChannel && writeAutoDelete)
811 delete writeChannel;
812
813 readChannel = readChan;
814 readAutoDelete = autoDeleteRead;
815
816 writeChannel = writeChan;
817 writeAutoDelete = autoDeleteWrite;
818
819 channelPointerMutex.EndWrite();
820
821 return IsOpen() && OnOpen();
822 }
823
824
OnOpen()825 PBoolean PIndirectChannel::OnOpen()
826 {
827 return PTrue;
828 }
829
830
SetReadChannel(PChannel * channel,bool autoDelete,bool closeExisting)831 bool PIndirectChannel::SetReadChannel(PChannel * channel, bool autoDelete, bool closeExisting)
832 {
833 PWriteWaitAndSignal mutex(channelPointerMutex);
834
835 if (closeExisting) {
836 if (readAutoDelete)
837 delete readChannel;
838 }
839 else {
840 if (readChannel != NULL)
841 return SetErrorValues(DeviceInUse, EEXIST);
842 }
843
844 readChannel = channel;
845 readAutoDelete = autoDelete;
846
847 return channel != NULL && channel->IsOpen();
848 }
849
850
SetWriteChannel(PChannel * channel,bool autoDelete,bool closeExisting)851 bool PIndirectChannel::SetWriteChannel(PChannel * channel, bool autoDelete, bool closeExisting)
852 {
853 PWriteWaitAndSignal mutex(channelPointerMutex);
854
855 if (closeExisting) {
856 if (writeAutoDelete)
857 delete writeChannel;
858 }
859 else {
860 if (writeChannel != NULL)
861 return SetErrorValues(DeviceInUse, EEXIST);
862 }
863
864 writeChannel = channel;
865 writeAutoDelete = autoDelete;
866
867 return channel != NULL && channel->IsOpen();
868 }
869
870
GetBaseReadChannel() const871 PChannel * PIndirectChannel::GetBaseReadChannel() const
872 {
873 PReadWaitAndSignal mutex(channelPointerMutex);
874 return readChannel != NULL ? readChannel->GetBaseReadChannel() : 0;
875 }
876
877
GetBaseWriteChannel() const878 PChannel * PIndirectChannel::GetBaseWriteChannel() const
879 {
880 PReadWaitAndSignal mutex(channelPointerMutex);
881 return writeChannel != NULL ? writeChannel->GetBaseWriteChannel() : 0;
882 }
883
884
885 ///////////////////////////////////////////////////////////////////////////////
886 // PFile
887
~PFile()888 PFile::~PFile()
889 {
890 Close();
891 }
892
893
Compare(const PObject & obj) const894 PObject::Comparison PFile::Compare(const PObject & obj) const
895 {
896 PAssert(PIsDescendant(&obj, PFile), PInvalidCast);
897 return path.Compare(((const PFile &)obj).path);
898 }
899
900
Rename(const PString & newname,PBoolean force)901 PBoolean PFile::Rename(const PString & newname, PBoolean force)
902 {
903 Close();
904
905 if (!ConvertOSError(Rename(path, newname, force) ? 0 : -1))
906 return PFalse;
907
908 path = path.GetDirectory() + newname;
909 return PTrue;
910 }
911
912
Close()913 PBoolean PFile::Close()
914 {
915 if (!IsOpen())
916 return SetErrorValues(NotOpen, EBADF);
917
918 flush();
919
920 #ifdef WOT_NO_FILESYSTEM
921 PBoolean ok = PTrue;
922 #else
923 PBoolean ok = ConvertOSError(_close(os_handle));
924 #endif
925
926 os_handle = -1;
927
928 if (removeOnClose)
929 Remove();
930
931 return ok;
932 }
933
934
Read(void * buffer,PINDEX amount)935 PBoolean PFile::Read(void * buffer, PINDEX amount)
936 {
937 if (!IsOpen())
938 return SetErrorValues(NotOpen, EBADF);
939
940 flush();
941 #ifdef WOT_NO_FILESYSTEM
942 lastReadCount = 0;
943 #else
944 lastReadCount = _read(GetHandle(), buffer, amount);
945 #endif
946 return ConvertOSError(lastReadCount, LastReadError) && lastReadCount > 0;
947 }
948
949
Write(const void * buffer,PINDEX amount)950 PBoolean PFile::Write(const void * buffer, PINDEX amount)
951 {
952 if (!IsOpen())
953 return SetErrorValues(NotOpen, EBADF);
954
955 flush();
956 #ifdef WOT_NO_FILESYSTEM
957 lastWriteCount = amount;
958 #else
959 lastWriteCount = _write(GetHandle(), buffer, amount);
960 #endif
961 return ConvertOSError(lastWriteCount, LastWriteError) && lastWriteCount >= amount;
962 }
963
964
Open(const PFilePath & name,OpenMode mode,int opts)965 PBoolean PFile::Open(const PFilePath & name, OpenMode mode, int opts)
966 {
967 Close();
968 SetFilePath(name);
969 return Open(mode, opts);
970 }
971
972
GetLength() const973 off_t PFile::GetLength() const
974 {
975 if (!IsOpen())
976 return -1;
977
978 #ifdef WOT_NO_FILESYSTEM
979 return 0;
980 #else
981 off_t pos = _lseek(GetHandle(), 0, SEEK_CUR);
982 off_t len = _lseek(GetHandle(), 0, SEEK_END);
983 PAssertOS(_lseek(GetHandle(), pos, SEEK_SET) != (off_t)-1);
984 return len;
985 #endif
986 }
987
988
IsEndOfFile() const989 PBoolean PFile::IsEndOfFile() const
990 {
991 if (!IsOpen())
992 return true;
993
994 ((PFile *)this)->flush();
995 return GetPosition() >= GetLength();
996 }
997
998
SetPosition(off_t pos,FilePositionOrigin origin)999 PBoolean PFile::SetPosition(off_t pos, FilePositionOrigin origin)
1000 {
1001 #ifdef WOT_NO_FILESYSTEM
1002 return PTrue;
1003 #else
1004 if (!IsOpen())
1005 return SetErrorValues(NotOpen, EBADF);
1006
1007 return _lseek(GetHandle(), pos, origin) != (off_t)-1;
1008 #endif
1009 }
1010
1011
Copy(const PFilePath & oldname,const PFilePath & newname,PBoolean force)1012 PBoolean PFile::Copy(const PFilePath & oldname, const PFilePath & newname, PBoolean force)
1013 {
1014 PFile oldfile(oldname, ReadOnly);
1015 if (!oldfile.IsOpen())
1016 return PFalse;
1017
1018 PFile newfile(newname,
1019 WriteOnly, Create|Truncate|(force ? MustExist : Exclusive));
1020 if (!newfile.IsOpen())
1021 return PFalse;
1022
1023 PCharArray buffer(10000);
1024
1025 off_t amount = oldfile.GetLength();
1026 while (amount > 10000) {
1027 if (!oldfile.Read(buffer.GetPointer(), 10000))
1028 return PFalse;
1029 if (!newfile.Write((const char *)buffer, 10000))
1030 return PFalse;
1031 amount -= 10000;
1032 }
1033
1034 if (!oldfile.Read(buffer.GetPointer(), (int)amount))
1035 return PFalse;
1036 if (!newfile.Write((const char *)buffer, (int)amount))
1037 return PFalse;
1038
1039 return newfile.Close();
1040 }
1041
1042
1043 // End Of File ///////////////////////////////////////////////////////////////
1044