3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
10 #if !defined(NO_SIGNALS) && (USE_SIO || !defined(SIGALRM) || !defined(SIGPIPE) || !defined(SIGINT))
17 static sigjmp_buf gBrokenCtrlJmp
;
19 static jmp_buf gBrokenCtrlJmp
;
20 #endif /* HAVE_SIGSETJMP */
23 BrokenCtrl(int UNUSED(signumIgnored
))
25 LIBNCFTP_USE_VAR(signumIgnored
); /* Shut up gcc */
27 siglongjmp(gBrokenCtrlJmp
, 1);
29 longjmp(gBrokenCtrlJmp
, 1);
30 #endif /* HAVE_SIGSETJMP */
33 #endif /* NO_SIGNALS */
36 /* A 'Response' parameter block is simply zeroed to be considered init'ed. */
42 rp
= (ResponsePtr
) calloc(SZ(1), sizeof(Response
));
44 InitLineList(&rp
->msg
);
51 /* If we don't print it to the screen, we may want to save it to our
55 TraceResponse(const FTPCIPtr cip
, ResponsePtr rp
)
62 PrintF(cip
, "%3d: %s\n", rp
->code
, lp
->line
);
63 for (lp
= lp
->next
; lp
!= NULL
; lp
= lp
->next
)
64 PrintF(cip
, " %s\n", lp
->line
);
74 PrintResponse(const FTPCIPtr cip
, LineListPtr llp
)
79 for (lp
= llp
->first
; lp
!= NULL
; lp
= lp
->next
)
80 PrintF(cip
, "%s\n", lp
->line
);
89 SaveLastResponse(const FTPCIPtr cip
, ResponsePtr rp
)
92 cip
->lastFTPCmdResultStr
[0] = '\0';
93 cip
->lastFTPCmdResultNum
= -1;
94 DisposeLineListContents(&cip
->lastFTPCmdResultLL
);
95 } else if ((rp
->msg
.first
== NULL
) || (rp
->msg
.first
->line
== NULL
)) {
96 cip
->lastFTPCmdResultStr
[0] = '\0';
97 cip
->lastFTPCmdResultNum
= rp
->code
;
98 DisposeLineListContents(&cip
->lastFTPCmdResultLL
);
100 (void) STRNCPY(cip
->lastFTPCmdResultStr
, rp
->msg
.first
->line
);
101 cip
->lastFTPCmdResultNum
= rp
->code
;
103 /* Dispose previous command's line list. */
104 DisposeLineListContents(&cip
->lastFTPCmdResultLL
);
106 /* Save this command's line list. */
107 cip
->lastFTPCmdResultLL
= rp
->msg
;
109 } /* SaveLastResponse */
114 DoneWithResponse(const FTPCIPtr cip
, ResponsePtr rp
)
116 /* Dispose space taken up by the Response, and clear it out
117 * again. For some reason, I like to return memory to zeroed
121 TraceResponse(cip
, rp
);
122 if (cip
->printResponseProc
!= 0) {
123 if ((rp
->printMode
& kResponseNoProc
) == 0)
124 (*cip
->printResponseProc
)(cip
, rp
);
126 if ((rp
->printMode
& kResponseNoSave
) == 0)
127 SaveLastResponse(cip
, rp
);
129 DisposeLineListContents(&rp
->msg
);
130 (void) memset(rp
, 0, sizeof(Response
));
133 } /* DoneWithResponse */
138 /* This takes an existing Response and recycles it, by clearing out
139 * the current contents.
142 ReInitResponse(const FTPCIPtr cip
, ResponsePtr rp
)
145 TraceResponse(cip
, rp
);
146 if (cip
->printResponseProc
!= 0) {
147 if ((rp
->printMode
& kResponseNoProc
) == 0)
148 (*cip
->printResponseProc
)(cip
, rp
);
150 if ((rp
->printMode
& kResponseNoSave
) == 0)
151 SaveLastResponse(cip
, rp
);
153 DisposeLineListContents(&rp
->msg
);
154 (void) memset(rp
, 0, sizeof(Response
));
156 } /* ReInitResponse */
163 /* Since the control stream is defined by the Telnet protocol (RFC 854),
164 * we follow Telnet rules when reading the control stream. We use this
165 * routine when we want to read a response from the host.
168 GetTelnetString(const FTPCIPtr cip
, char *str
, size_t siz
, FILE *cin
, FILE *cout
)
176 --siz
; /* We'll need room for the \0. */
178 if ((cin
== NULL
) || (cout
== NULL
)) {
183 for (n
= (size_t)0, eofError
= 0; ; ) {
190 } else if (c
== '\r') {
191 /* A telnet string can have a CR by itself. But to denote that,
192 * the protocol uses \r\0; an end of line is denoted \r\n.
196 /* Had \r\n, so done. */
198 } else if (c
== EOF
) {
200 } else if (c
== '\0') {
204 /* Telnet protocol violation! */
207 } else if (c
== '\n') {
208 /* Really shouldn't get here. If we do, the other side
209 * violated the TELNET protocol, since eoln's are CR/LF,
212 PrintF(cip
, "TELNET protocol violation: raw LF.\n");
214 } else if (c
== IAC
) {
215 /* Since the control connection uses the TELNET protocol,
216 * we have to handle some of its commands ourselves.
217 * IAC is the protocol's escape character, meaning that
218 * the next character after the IAC (Interpret as Command)
219 * character is a telnet command. But, if there just
220 * happened to be a character in the text stream with the
221 * same numerical value of IAC, 255, the sender denotes
222 * that by having an IAC followed by another IAC.
225 /* Get the telnet command. */
231 /* Get the option code. */
234 /* Tell the other side that we don't want
235 * to do what they're offering to do.
237 (void) fprintf(cout
, "%c%c%c",IAC
,DONT
,c
);
242 /* Get the option code. */
245 /* The other side said they are DOing (or not)
246 * something, which would happen if our side
247 * asked them to. Since we didn't do that,
248 * ask them to not do this option.
250 (void) fprintf(cout
, "%c%c%c",IAC
,WONT
,c
);
258 /* Just add this character, since it was most likely
259 * just an escaped IAC character.
265 /* If the buffer supplied has room, add this character to it. */
276 } /* GetTelnetString */
278 #endif /* NO_SIGNALS */
282 /* Returns 0 if a response was read, or (-1) if an error occurs.
283 * This reads the entire response text into a LineList, which is kept
284 * in the 'Response' structure.
287 GetResponse(const FTPCIPtr cip
, ResponsePtr rp
)
294 volatile FTPCIPtr vcip
;
297 #else /* NO_SIGNALS */
298 volatile FTPSigProc osigpipe
;
300 #endif /* NO_SIGNALS */
302 /* RFC 959 states that a reply may span multiple lines. A single
303 * line message would have the 3-digit code <space> then the msg.
304 * A multi-line message would have the code <dash> and the first
305 * line of the msg, then additional lines, until the last line,
306 * which has the code <space> and last line of the msg.
311 * 234 A line beginning with numbers
317 #else /* NO_SIGNALS */
318 osigpipe
= (volatile FTPSigProc
) signal(SIGPIPE
, BrokenCtrl
);
321 #ifdef HAVE_SIGSETJMP
322 sj
= sigsetjmp(gBrokenCtrlJmp
, 1);
324 sj
= setjmp(gBrokenCtrlJmp
);
325 #endif /* HAVE_SIGSETJMP */
328 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
329 FTPShutdownHost(vcip
);
330 vcip
->errNo
= kErrRemoteHostClosedConnection
;
333 #endif /* NO_SIGNALS */
338 if (cip
->dataTimedOut
> 0) {
339 /* Give up immediately unless the server had already
340 * sent a message. Odds are since the data is timed
341 * out, so is the control.
343 if (SWaitUntilReadyForReading(cip
->ctrlSocketR
, 0) == 0) {
345 Error(cip
, kDontPerror
, "Could not read reply from control connection -- timed out.\n");
346 FTPShutdownHost(vcip
);
347 cip
->errNo
= kErrControlTimedOut
;
351 result
= SReadline(&cip
->ctrlSrl
, str
, sizeof(str
) - 1);
352 if (result
== kTimeoutErr
) {
354 Error(cip
, kDontPerror
, "Could not read reply from control connection -- timed out.\n");
355 FTPShutdownHost(vcip
);
356 cip
->errNo
= kErrControlTimedOut
;
358 } else if (result
== 0) {
362 if (rp
->eofOkay
== 0)
363 Error(cip
, kDontPerror
, "Remote host has closed the connection.\n");
364 FTPShutdownHost(vcip
);
365 cip
->errNo
= kErrRemoteHostClosedConnection
;
367 } else if (result
< 0) {
369 Error(cip
, kDoPerror
, "Could not read reply from control connection");
370 FTPShutdownHost(vcip
);
371 cip
->errNo
= kErrInvalidReplyFromServer
;
375 if (str
[result
- 1] == '\n')
376 str
[result
- 1] = '\0';
378 #else /* NO_SIGNALS */
379 /* Get the first line of the response. */
380 eofError
= GetTelnetString(cip
, str
, sizeof(str
), cip
->cin
, cip
->cout
);
385 /* No bytes read for reply, and EOF detected. */
387 if (rp
->eofOkay
== 0)
388 Error(cip
, kDontPerror
, "Remote host has closed the connection.\n");
389 FTPShutdownHost(vcip
);
390 cip
->errNo
= kErrRemoteHostClosedConnection
;
391 (void) signal(SIGPIPE
, osigpipe
);
395 #endif /* NO_SIGNALS */
397 if (!isdigit((int) *cp
)) {
398 Error(cip
, kDontPerror
, "Invalid reply: \"%s\"\n", cp
);
399 cip
->errNo
= kErrInvalidReplyFromServer
;
401 (void) signal(SIGPIPE
, osigpipe
);
406 rp
->codeType
= *cp
- '0';
408 continuation
= (*cp
== '-');
410 (void) STRNCPY(code
, str
);
411 rp
->code
= atoi(code
);
412 (void) AddLine(&rp
->msg
, cp
);
414 /* Read reply, but EOF was there also. */
418 while (continuation
) {
421 result
= SReadline(&cip
->ctrlSrl
, str
, sizeof(str
) - 1);
422 if (result
== kTimeoutErr
) {
424 Error(cip
, kDontPerror
, "Could not read reply from control connection -- timed out.\n");
425 FTPShutdownHost(vcip
);
426 cip
->errNo
= kErrControlTimedOut
;
428 } else if (result
== 0) {
432 if (rp
->eofOkay
== 0)
433 Error(cip
, kDontPerror
, "Remote host has closed the connection.\n");
434 FTPShutdownHost(vcip
);
435 cip
->errNo
= kErrRemoteHostClosedConnection
;
437 } else if (result
< 0) {
439 Error(cip
, kDoPerror
, "Could not read reply from control connection");
440 FTPShutdownHost(vcip
);
441 cip
->errNo
= kErrInvalidReplyFromServer
;
445 if (str
[result
- 1] == '\n')
446 str
[result
- 1] = '\0';
447 #else /* NO_SIGNALS */
448 eofError
= GetTelnetString(cip
, str
, sizeof(str
), cip
->cin
, cip
->cout
);
450 /* Read reply, but EOF was there also. */
454 #endif /* NO_SIGNALS */
456 if (strncmp(code
, cp
, SZ(3)) == 0) {
462 (void) AddLine(&rp
->msg
, cp
);
465 if (rp
->code
== 421) {
467 * 421 Service not available, closing control connection.
468 * This may be a reply to any command if the service knows it
471 if (rp
->eofOkay
== 0)
472 Error(cip
, kDontPerror
, "Remote host has closed the connection.\n");
473 FTPShutdownHost(vcip
);
474 cip
->errNo
= kErrRemoteHostClosedConnection
;
476 (void) signal(SIGPIPE
, osigpipe
);
482 (void) signal(SIGPIPE
, osigpipe
);
490 /* This creates the complete command text to send, and writes it
496 SendCommand(const FTPCIPtr cip
, const char *cmdspec
, va_list ap
)
501 if (cip
->ctrlSocketW
!= kClosedFileDescriptor
) {
502 #ifdef HAVE_VSNPRINTF
503 (void) vsnprintf(command
, sizeof(command
) - 1, cmdspec
, ap
);
504 command
[sizeof(command
) - 1] = '\0';
506 (void) vsprintf(command
, cmdspec
, ap
);
508 if ((strncmp(command
, "PASS", SZ(4)) != 0) || ((strcmp(cip
->user
, "anonymous") == 0) && (cip
->firewallType
== kFirewallNotInUse
)))
509 PrintF(cip
, "Cmd: %s\n", command
);
511 PrintF(cip
, "Cmd: %s\n", "PASS xxxxxxxx");
512 (void) STRNCAT(command
, "\r\n"); /* Use TELNET end-of-line. */
513 cip
->lastFTPCmdResultStr
[0] = '\0';
514 cip
->lastFTPCmdResultNum
= -1;
516 result
= SWrite(cip
->ctrlSocketW
, command
, strlen(command
), (int) cip
->ctrlTimeout
, 0);
519 cip
->errNo
= kErrSocketWriteFailed
;
520 Error(cip
, kDoPerror
, "Could not write to control stream.\n");
525 return (kErrNotConnected
);
528 #else /* NO_SIGNALS */
531 SendCommand(const FTPCIPtr cip
, const char *cmdspec
, va_list ap
)
535 volatile FTPCIPtr vcip
;
536 volatile FTPSigProc osigpipe
;
539 if (cip
->cout
!= NULL
) {
540 #ifdef HAVE_VSNPRINTF
541 (void) vsnprintf(command
, sizeof(command
) - 1, cmdspec
, ap
);
542 command
[sizeof(command
) - 1] = '\0';
544 (void) vsprintf(command
, cmdspec
, ap
);
546 if ((strncmp(command
, "PASS", SZ(4)) != 0) || ((strcmp(cip
->user
, "anonymous") == 0) && (cip
->firewallType
== kFirewallNotInUse
)))
547 PrintF(cip
, "Cmd: %s\n", command
);
549 PrintF(cip
, "Cmd: %s\n", "PASS xxxxxxxx");
550 (void) STRNCAT(command
, "\r\n"); /* Use TELNET end-of-line. */
551 cip
->lastFTPCmdResultStr
[0] = '\0';
552 cip
->lastFTPCmdResultNum
= -1;
554 osigpipe
= (volatile FTPSigProc
) signal(SIGPIPE
, BrokenCtrl
);
557 #ifdef HAVE_SIGSETJMP
558 sj
= sigsetjmp(gBrokenCtrlJmp
, 1);
560 sj
= setjmp(gBrokenCtrlJmp
);
561 #endif /* HAVE_SIGSETJMP */
564 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
565 FTPShutdownHost(vcip
);
566 if (vcip
->eofOkay
== 0) {
567 Error(cip
, kDontPerror
, "Remote host has closed the connection.\n");
568 vcip
->errNo
= kErrRemoteHostClosedConnection
;
574 result
= fputs(command
, cip
->cout
);
576 (void) signal(SIGPIPE
, osigpipe
);
577 cip
->errNo
= kErrSocketWriteFailed
;
578 Error(cip
, kDoPerror
, "Could not write to control stream.\n");
581 result
= fflush(cip
->cout
);
583 (void) signal(SIGPIPE
, osigpipe
);
584 cip
->errNo
= kErrSocketWriteFailed
;
585 Error(cip
, kDoPerror
, "Could not write to control stream.\n");
588 (void) signal(SIGPIPE
, osigpipe
);
591 return (kErrNotConnected
);
593 #endif /* NO_SIGNALS */
597 /* For "simple" (i.e. not data transfer) commands, this routine is used
598 * to send the command and receive one response. It returns the codeType
599 * field of the 'Response' as the result, or a negative number upon error.
603 FTPCmd(const FTPCIPtr cip
, const char *const cmdspec
, ...)
610 return (kErrBadParameter
);
611 if (strcmp(cip
->magic
, kLibraryMagic
))
612 return (kErrBadMagic
);
616 result
= kErrMallocFailed
;
617 cip
->errNo
= kErrMallocFailed
;
618 Error(cip
, kDontPerror
, "Malloc failed.\n");
622 va_start(ap
, cmdspec
);
624 if (cip
->ctrlTimeout
> 0)
625 (void) alarm(cip
->ctrlTimeout
);
626 #endif /* NO_SIGNALS */
627 result
= SendCommand(cip
, cmdspec
, ap
);
631 if (cip
->ctrlTimeout
> 0)
633 #endif /* NO_SIGNALS */
637 /* Get the response to the command we sent. */
638 result
= GetResponse(cip
, rp
);
640 if (cip
->ctrlTimeout
> 0)
642 #endif /* NO_SIGNALS */
644 if (result
== kNoErr
)
645 result
= rp
->codeType
;
646 DoneWithResponse(cip
, rp
);
653 /* This is for debugging the library -- don't use. */
656 FTPCmdNoResponse(const FTPCIPtr cip
, const char *const cmdspec
, ...)
661 return (kErrBadParameter
);
662 if (strcmp(cip
->magic
, kLibraryMagic
))
663 return (kErrBadMagic
);
665 va_start(ap
, cmdspec
);
667 if (cip
->ctrlTimeout
> 0)
668 (void) alarm(cip
->ctrlTimeout
);
669 #endif /* NO_SIGNALS */
670 (void) SendCommand(cip
, cmdspec
, ap
);
672 if (cip
->ctrlTimeout
> 0)
674 #endif /* NO_SIGNALS */
678 } /* FTPCmdNoResponse */
684 WaitResponse(const FTPCIPtr cip
, unsigned int sec
)
692 fd
= cip
->ctrlSocketR
;
693 #else /* NO_SIGNALS */
694 if (cip
->cin
== NULL
)
696 fd
= fileno(cip
->cin
);
697 #endif /* NO_SIGNALS */
702 tv
.tv_sec
= (unsigned long) sec
;
704 result
= select(fd
+ 1, SELECT_TYPE_ARG234
&ss
, NULL
, NULL
, &tv
);
711 /* For "simple" (i.e. not data transfer) commands, this routine is used
712 * to send the command and receive one response. It returns the codeType
713 * field of the 'Response' as the result, or a negative number upon error.
718 RCmd(const FTPCIPtr cip
, ResponsePtr rp
, const char *cmdspec
, ...)
724 return (kErrBadParameter
);
725 if (strcmp(cip
->magic
, kLibraryMagic
))
726 return (kErrBadMagic
);
728 va_start(ap
, cmdspec
);
730 if (cip
->ctrlTimeout
> 0)
731 (void) alarm(cip
->ctrlTimeout
);
732 #endif /* NO_SIGNALS */
733 result
= SendCommand(cip
, cmdspec
, ap
);
737 if (cip
->ctrlTimeout
> 0)
739 #endif /* NO_SIGNALS */
743 /* Get the response to the command we sent. */
744 result
= GetResponse(cip
, rp
);
746 if (cip
->ctrlTimeout
> 0)
748 #endif /* NO_SIGNALS */
750 if (result
== kNoErr
)
751 result
= rp
->codeType
;
757 /* Returns -1 if an error occurred, or 0 if not.
758 * This differs from RCmd, which returns the code class of a response.
763 FTPStartDataCmd(const FTPCIPtr cip
, int netMode
, int type
, longest_int startPoint
, const char *cmdspec
, ...)
771 return (kErrBadParameter
);
772 if (strcmp(cip
->magic
, kLibraryMagic
))
773 return (kErrBadMagic
);
775 result
= FTPSetTransferType(cip
, type
);
779 /* Re-set the cancellation flag. */
782 /* To transfer data, we do these things in order as specifed by
785 * First, we tell the other side to set up a data line. This
786 * is done below by calling OpenDataConnection(), which sets up
787 * the socket. When we do that, the other side detects a connection
788 * attempt, so it knows we're there. Then tell the other side
789 * (by using listen()) that we're willing to receive a connection
793 if ((result
= OpenDataConnection(cip
, cip
->dataPortMode
)) < 0)
796 /* If asked, attempt to start at a later position in the remote file. */
797 if (startPoint
!= (longest_int
) 0) {
798 if ((startPoint
== kSizeUnknown
) || ((result
= SetStartOffset(cip
, startPoint
)) != 0))
799 startPoint
= (longest_int
) 0;
801 cip
->startPoint
= startPoint
;
803 /* Now we tell the server what we want to do. This sends the
804 * the type of transfer we want (RETR, STOR, LIST, etc) and the
805 * parameters for that (files to send, directories to list, etc).
807 va_start(ap
, cmdspec
);
809 if (cip
->ctrlTimeout
> 0)
810 (void) alarm(cip
->ctrlTimeout
);
811 #endif /* NO_SIGNALS */
812 result
= SendCommand(cip
, cmdspec
, ap
);
816 if (cip
->ctrlTimeout
> 0)
818 #endif /* NO_SIGNALS */
822 /* Get the response to the transfer command we sent, to see if
823 * they can accomodate the request. If everything went okay,
824 * we will get a preliminary response saying that the transfer
825 * initiation was successful and that the data is there for
826 * reading (for retrieves; for sends, they will be waiting for
827 * us to send them something).
831 Error(cip
, kDontPerror
, "Malloc failed.\n");
832 cip
->errNo
= kErrMallocFailed
;
836 result
= GetResponse(cip
, rp
);
838 if (cip
->ctrlTimeout
> 0)
840 #endif /* NO_SIGNALS */
844 respCode
= rp
->codeType
;
845 DoneWithResponse(cip
, rp
);
848 cip
->errNo
= kErrCouldNotStartDataTransfer
;
853 /* Now we accept the data connection that the other side is offering
854 * to us. Then we can do the actual I/O on the data we want.
856 cip
->netMode
= netMode
;
857 if ((result
= AcceptDataConnection(cip
)) < 0)
862 (void) FTPEndDataCmd(cip
, 0);
864 } /* FTPStartDataCmd */
870 FTPAbortDataTransfer(const FTPCIPtr cip
)
875 if (cip
->dataSocket
!= kClosedFileDescriptor
) {
876 PrintF(cip
, "Starting abort sequence.\n");
877 SendTelnetInterrupt(cip
); /* Probably could get by w/o doing this. */
879 result
= FTPCmdNoResponse(cip
, "ABOR");
880 if (result
!= kNoErr
) {
881 /* Linger could cause close to block, so unset it. */
882 (void) SetLinger(cip
, cip
->dataSocket
, 0);
883 CloseDataConnection(cip
);
884 PrintF(cip
, "Could not send abort command.\n");
888 if (cip
->abortTimeout
> 0) {
889 result
= WaitResponse(cip
, (unsigned int) cip
->abortTimeout
);
891 /* Error or no response received to ABOR in time. */
892 (void) SetLinger(cip
, cip
->dataSocket
, 0);
893 CloseDataConnection(cip
);
894 PrintF(cip
, "No response received to abort request.\n");
901 Error(cip
, kDontPerror
, "Malloc failed.\n");
902 cip
->errNo
= kErrMallocFailed
;
907 result
= GetResponse(cip
, rp
);
909 /* Shouldn't happen, and doesn't matter if it does. */
910 (void) SetLinger(cip
, cip
->dataSocket
, 0);
911 CloseDataConnection(cip
);
912 PrintF(cip
, "Invalid response to abort request.\n");
913 DoneWithResponse(cip
, rp
);
916 DoneWithResponse(cip
, rp
);
918 /* A response to the abort request has been received.
919 * Now the only thing left to do is close the data
920 * connection, making sure to turn off linger mode
921 * since we don't care about straggling data bits.
923 (void) SetLinger(cip
, cip
->dataSocket
, 0);
924 CloseDataConnection(cip
); /* Must close (by protocol). */
925 PrintF(cip
, "End abort.\n");
927 } /* FTPAbortDataTransfer */
933 FTPEndDataCmd(const FTPCIPtr cip
, int didXfer
)
940 return (kErrBadParameter
);
941 if (strcmp(cip
->magic
, kLibraryMagic
))
942 return (kErrBadMagic
);
944 CloseDataConnection(cip
);
947 /* Get the response to the data transferred. Most likely a message
948 * saying that the transfer completed succesfully. However, if
949 * we tried to abort the transfer using ABOR, we will have a response
950 * to that command instead.
954 Error(cip
, kDontPerror
, "Malloc failed.\n");
955 cip
->errNo
= kErrMallocFailed
;
959 result
= GetResponse(cip
, rp
);
962 respCode
= rp
->codeType
;
963 DoneWithResponse(cip
, rp
);
965 cip
->errNo
= kErrDataTransferFailed
;
972 } /* FTPEndDataCmd */
978 BufferGets(char *buf
, size_t bufsize
, int inStream
, char *secondaryBuf
, char **secBufPtr
, char **secBufLimit
, size_t secBufSize
)
990 dstlim
= dst
+ bufsize
- 1; /* Leave room for NUL. */
992 for ( ; dst
< dstlim
; ) {
993 if (src
>= (*secBufLimit
)) {
994 /* Fill the buffer. */
996 /* Don't need to poll it here. The routines that use BufferGets don't
997 * need any special processing during timeouts (i.e. progress reports),
998 * so go ahead and just let it block until there is data to read.
1000 nr
= (int) read(inStream
, secondaryBuf
, secBufSize
);
1005 } else if (nr
< 0) {
1010 (*secBufPtr
) = secondaryBuf
;
1011 (*secBufLimit
) = secondaryBuf
+ nr
;
1013 if (nr
< (int) secBufSize
)
1020 /* *dst++ = *src++; */ ++src
;
1030 len
= (int) (dst
- buf
);
1033 if ((len
== 0) && (haveEof
== 1))
1035 return (len
); /* May be zero, if a blank line. */