+int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
+{
+ long WriteRemain;
+ int n;
+
+ if ((FB == NULL) || (FB->Buf == NULL))
+ return -1;
+
+ if (FB->ReadWritePointer != NULL)
+ {
+ WriteRemain = FB->Buf->BufUsed -
+ (FB->ReadWritePointer -
+ FB->Buf->buf);
+ }
+ else {
+ FB->ReadWritePointer = FB->Buf->buf;
+ WriteRemain = FB->Buf->BufUsed;
+ }
+
+ n = write(fd, FB->ReadWritePointer, WriteRemain);
+ if (n > 0) {
+ FB->ReadWritePointer += n;
+
+ if (FB->ReadWritePointer ==
+ FB->Buf->buf + FB->Buf->BufUsed)
+ {
+ FlushStrBuf(FB->Buf);
+ FB->ReadWritePointer = NULL;
+ return 0;
+ }
+ // check whether we've got something to write
+ // get the maximum chunk plus the pointer we can send
+ // write whats there
+ // if not all was sent, remember the send pointer for the next time
+ return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
+ }
+ return n;
+}
+
+/**
+ * @ingroup StrBuf_IO
+ * @brief extract a "next line" from Buf; Ptr to persist across several iterations
+ * @param LineBuf your line will be copied here.
+ * @param FB BLOB with lines of text...
+ * @param Ptr moved arround to keep the next-line across several iterations
+ * has to be &NULL on start; will be &NotNULL on end of buffer
+ * @returns size of copied buffer
+ */
+eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
+{
+ const char *aptr, *ptr, *eptr;
+ char *optr, *xptr;
+
+ if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
+ return eReadFail;
+
+
+ if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
+ FB->ReadWritePointer = StrBufNOTNULL;
+ return eReadFail;
+ }
+
+ FlushStrBuf(LineBuf);
+ if (FB->ReadWritePointer == NULL)
+ ptr = aptr = FB->Buf->buf;
+ else
+ ptr = aptr = FB->ReadWritePointer;
+
+ optr = LineBuf->buf;
+ eptr = FB->Buf->buf + FB->Buf->BufUsed;
+ xptr = LineBuf->buf + LineBuf->BufSize - 1;
+
+ while ((ptr <= eptr) &&
+ (*ptr != '\n') &&
+ (*ptr != '\r') )
+ {
+ *optr = *ptr;
+ optr++; ptr++;
+ if (optr == xptr) {
+ LineBuf->BufUsed = optr - LineBuf->buf;
+ IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
+ optr = LineBuf->buf + LineBuf->BufUsed;
+ xptr = LineBuf->buf + LineBuf->BufSize - 1;
+ }
+ }
+
+ if (ptr >= eptr) {
+ if (optr > LineBuf->buf)
+ optr --;
+ if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
+ LineBuf->BufUsed = optr - LineBuf->buf;
+ *optr = '\0';
+ if ((FB->ReadWritePointer != NULL) &&
+ (FB->ReadWritePointer != FB->Buf->buf))
+ {
+ /* Ok, the client application read all the data
+ it was interested in so far. Since there is more to read,
+ we now shrink the buffer, and move the rest over.
+ */
+ StrBufCutLeft(FB->Buf,
+ FB->ReadWritePointer - FB->Buf->buf);
+ FB->ReadWritePointer = FB->Buf->buf;
+ }
+ return eMustReadMore;
+ }
+ }
+ LineBuf->BufUsed = optr - LineBuf->buf;
+ *optr = '\0';
+ if ((ptr <= eptr) && (*ptr == '\r'))
+ ptr ++;
+ if ((ptr <= eptr) && (*ptr == '\n'))
+ ptr ++;
+
+ if (ptr < eptr) {
+ FB->ReadWritePointer = ptr;
+ }
+ else {
+ FlushStrBuf(FB->Buf);
+ FB->ReadWritePointer = NULL;
+ }
+
+ return eReadSuccess;
+}
+
+/**
+ * @ingroup StrBuf_CHUNKED_IO
+ * @brief check whether the chunk-buffer has more data waiting or not.
+ * @param FB Chunk-Buffer to inspect
+ */
+eReadState StrBufCheckBuffer(IOBuffer *FB)
+{
+ if (FB == NULL)
+ return eReadFail;
+ if (FB->Buf->BufUsed == 0)
+ return eReadSuccess;
+ if (FB->ReadWritePointer == NULL)
+ return eBufferNotEmpty;
+ if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
+ return eBufferNotEmpty;
+ return eReadSuccess;
+}
+
+long IOBufferStrLength(IOBuffer *FB)
+{
+ if ((FB == NULL) || (FB->Buf == NULL))
+ return 0;
+ if (FB->ReadWritePointer == NULL)
+ return StrLength(FB->Buf);
+
+ return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
+}
+
+inline static void FDIOBufferFlush(FDIOBuffer *FDB)
+{
+ memset(FDB, 0, sizeof(FDIOBuffer));
+ FDB->OtherFD = -1;
+ FDB->SplicePipe[0] = -1;
+ FDB->SplicePipe[1] = -1;
+}
+
+void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
+{
+ FDIOBufferFlush(FDB);
+
+ FDB->TotalSendSize = TotalSendSize;
+ if (TotalSendSize > 0)
+ FDB->ChunkSize = TotalSendSize;
+ else
+ {
+ TotalSendSize = SIZ * 10;
+ FDB->ChunkSize = TotalSendSize;
+ }
+ FDB->IOB = IO;
+
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ pipe(FDB->SplicePipe);
+ else
+#endif
+ FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
+
+ FDB->OtherFD = FD;
+}
+
+void FDIOBufferDelete(FDIOBuffer *FDB)
+{
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ {
+ if (FDB->SplicePipe[0] > 0)
+ close(FDB->SplicePipe[0]);
+ if (FDB->SplicePipe[1] > 0)
+ close(FDB->SplicePipe[1]);
+ }
+ else
+#endif
+ FreeStrBuf(&FDB->ChunkBuffer);
+
+ if (FDB->OtherFD > 0)
+ close(FDB->OtherFD);
+ FDIOBufferFlush(FDB);
+}
+
+int FileSendChunked(FDIOBuffer *FDB, const char **Err)
+{
+ ssize_t sent, pipesize;
+
+ if (FDB->TotalSendSize > 0)
+ {
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ {
+ if (FDB->PipeSize == 0)
+ {
+ pipesize = splice(FDB->OtherFD,
+ &FDB->TotalSentAlready,
+ FDB->SplicePipe[1],
+ NULL,
+ FDB->ChunkSendRemain,
+ SPLICE_F_MOVE);
+
+ if (pipesize == -1)
+ {
+ *Err = strerror(errno);
+ return pipesize;
+ }
+ FDB->PipeSize = pipesize;
+ }
+ sent = splice(FDB->SplicePipe[0],
+ NULL,
+ FDB->IOB->fd,
+ NULL,
+ FDB->PipeSize,
+ SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+ if (sent == -1)
+ {
+ *Err = strerror(errno);
+ return sent;
+ }
+ FDB->PipeSize -= sent;
+ FDB->ChunkSendRemain -= sent;
+ return sent;
+ }
+ else
+#endif
+ {
+ char *pRead;
+ long nRead = 0;
+
+ pRead = FDB->ChunkBuffer->buf;
+ while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
+ {
+ nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
+ if (nRead > 0) {
+ FDB->ChunkBuffer->BufUsed += nRead;
+ FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+ }
+ else if (nRead == 0) {}
+ else return nRead;
+ }
+
+ nRead = write(FDB->IOB->fd,
+ FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
+ FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
+
+ if (nRead >= 0) {
+ FDB->TotalSentAlready += nRead;
+ FDB->ChunkSendRemain -= nRead;
+ return FDB->ChunkSendRemain;
+ }
+ else {
+ return nRead;
+ }
+ }
+ }
+ else
+ {
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ {
+ if (FDB->PipeSize == 0)
+ {
+ pipesize = splice(FDB->OtherFD,
+ &FDB->TotalSentAlready,
+ FDB->SplicePipe[1],
+ NULL,
+ SIZ * 10,
+ SPLICE_F_MOVE);
+
+ if (pipesize == -1)
+ {
+ *Err = strerror(errno);
+ return pipesize;
+ }
+ FDB->PipeSize = pipesize;
+ if (pipesize == 0)
+ return -1;
+ }
+ sent = splice(FDB->SplicePipe[0],
+ NULL,
+ FDB->IOB->fd,
+ NULL,
+ FDB->PipeSize,
+ SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+ if (sent == -1)
+ {
+ *Err = strerror(errno);
+ return sent;
+ }
+ FDB->PipeSize -= sent;
+ FDB->ChunkSendRemain -= sent;
+ return sent;
+ }
+ else
+#endif
+ {
+ char *pRead;
+ long nRead = 0;
+
+ pRead = FDB->ChunkBuffer->buf;
+ while ((FDB->ChunkSendRemain == 0) &&
+ (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
+ (nRead >= 0))
+ {
+ FDB->TotalSentAlready = 0;
+ nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
+ if (nRead > 0) {
+ FDB->ChunkBuffer->BufUsed += nRead;
+ FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+ FDB->ChunkSendRemain += nRead;
+ }
+ else if (nRead == 0)
+ {
+ return -1;
+ }
+ else
+ {
+ *Err = strerror(errno);
+ return nRead;
+ }
+ }
+
+ nRead = write(FDB->IOB->fd,
+ FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
+ FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
+
+ if (nRead >= 0) {
+ FDB->TotalSentAlready += nRead;
+ FDB->ChunkSendRemain -= nRead;
+ if (FDB->ChunkSendRemain == 0)
+ {
+ FDB->ChunkBuffer->BufUsed = 0;
+ FDB->TotalSentAlready = 0;
+ }
+ return FDB->ChunkSendRemain;
+ }
+ else {
+ return nRead;
+ }
+ }
+ }