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