pjs/lib/xp/xp_linebuf.c

309 строки
9.6 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
/* xp_linebuf.c -- general line-buffering and de-buffering of text streams.
Used by libmime, llibmsg, and others.
This stuff used to be in libmsg/msgutils.c, but it's really not
mail-specific. And I had to move it to get libmime to work in the
absence of libmsg, so in the meantime, I changed the prefix from MSG_
to XP_.
*/
#include "xp_mcom.h"
#include "xp_linebuf.h"
extern int MK_OUT_OF_MEMORY;
/* Amazingly enough, these seem only to be defined in fe_proto.h, which is
totally the wrong thing to be including here.
*/
#define CR '\015'
#define LF '\012'
#define CRLF "\015\012"
#ifdef XP_MAC
# define LINEBREAK "\012"
# define LINEBREAK_LEN 1
#else
# if defined(XP_WIN) || defined(XP_OS2)
# define LINEBREAK "\015\012"
# define LINEBREAK_LEN 2
# else
# ifdef XP_UNIX
# define LINEBREAK "\012"
# define LINEBREAK_LEN 1
# endif /* XP_UNIX */
# endif /* XP_WIN */
#endif /* XP_MAC */
/* Buffer management.
Why do I feel like I've written this a hundred times before?
*/
int
XP_GrowBuffer (uint32 desired_size, uint32 element_size, uint32 quantum,
char **buffer, uint32 *size)
{
if (*size <= desired_size)
{
char *new_buf;
uint32 increment = desired_size - *size;
if (increment < quantum) /* always grow by a minimum of N bytes */
increment = quantum;
#ifdef TESTFORWIN16
if (((*size + increment) * (element_size / sizeof(char))) >= 64000)
{
/* Be safe on WIN16 */
XP_ASSERT(0);
return MK_OUT_OF_MEMORY;
}
#endif /* DEBUG */
new_buf = (*buffer
? (char *) XP_REALLOC (*buffer, (*size + increment)
* (element_size / sizeof(char)))
: (char *) XP_ALLOC ((*size + increment)
* (element_size / sizeof(char))));
if (! new_buf)
return MK_OUT_OF_MEMORY;
*buffer = new_buf;
*size += increment;
}
return 0;
}
#if 0 /* #### what is this?? and what is it doing here?? */
extern XP_Bool NET_POP3TooEarlyForEnd(int32 len);
#endif
/* Take the given buffer, tweak the newlines at the end if necessary, and
send it off to the given routine. We are guaranteed that the given
buffer has allocated space for at least one more character at the end. */
static int
xp_convert_and_send_buffer(char* buf, uint32 length,
XP_Bool convert_newlines_p,
int32 (*per_line_fn) (char *line,
uint32 line_length,
void *closure),
void *closure)
{
/* Convert the line terminator to the native form.
*/
char* newline;
XP_ASSERT(buf && length > 0);
if (!buf || length <= 0) return -1;
newline = buf + length;
XP_ASSERT(newline[-1] == CR || newline[-1] == LF);
if (newline[-1] != CR && newline[-1] != LF) return -1;
/* update count of bytes parsed adding/removing CR or LF */
#if 0 /* #### what is this?? and what is it doing here?? */
NET_POP3TooEarlyForEnd(length);
#endif
if (!convert_newlines_p)
{
}
#if (LINEBREAK_LEN == 1)
else if ((newline - buf) >= 2 &&
newline[-2] == CR &&
newline[-1] == LF)
{
/* CRLF -> CR or LF */
buf [length - 2] = LINEBREAK[0];
length--;
}
else if (newline > buf + 1 &&
newline[-1] != LINEBREAK[0])
{
/* CR -> LF or LF -> CR */
buf [length - 1] = LINEBREAK[0];
}
#else
else if (((newline - buf) >= 2 && newline[-2] != CR) ||
((newline - buf) >= 1 && newline[-1] != LF))
{
/* LF -> CRLF or CR -> CRLF */
length++;
buf[length - 2] = LINEBREAK[0];
buf[length - 1] = LINEBREAK[1];
}
#endif
return (*per_line_fn)(buf, length, closure);
}
/* SI::BUFFERED-STREAM-MIXIN
Why do I feel like I've written this a hundred times before?
*/
int
XP_LineBuffer (const char *net_buffer, int32 net_buffer_size,
char **bufferP, uint32 *buffer_sizeP, uint32 *buffer_fpP,
XP_Bool convert_newlines_p,
int32 (*per_line_fn) (char *line, uint32 line_length,
void *closure),
void *closure)
{
int status = 0;
if (*buffer_fpP > 0 && *bufferP && (*bufferP)[*buffer_fpP - 1] == CR &&
net_buffer_size > 0 && net_buffer[0] != LF) {
/* The last buffer ended with a CR. The new buffer does not start
with a LF. This old buffer should be shipped out and discarded. */
XP_ASSERT(*buffer_sizeP > *buffer_fpP);
if (*buffer_sizeP <= *buffer_fpP) return -1;
status = xp_convert_and_send_buffer(*bufferP, *buffer_fpP,
convert_newlines_p,
per_line_fn, closure);
if (status < 0) return status;
*buffer_fpP = 0;
}
while (net_buffer_size > 0)
{
const char *net_buffer_end = net_buffer + net_buffer_size;
const char *newline = 0;
const char *s;
for (s = net_buffer; s < net_buffer_end; s++)
{
/* Move forward in the buffer until the first newline.
Stop when we see CRLF, CR, or LF, or the end of the buffer.
*But*, if we see a lone CR at the *very end* of the buffer,
treat this as if we had reached the end of the buffer without
seeing a line terminator. This is to catch the case of the
buffers splitting a CRLF pair, as in "FOO\r\nBAR\r" "\nBAZ\r\n".
*/
if (*s == CR || *s == LF)
{
newline = s;
if (newline[0] == CR)
{
if (s == net_buffer_end - 1)
{
/* CR at end - wait for the next character. */
newline = 0;
break;
}
else if (newline[1] == LF)
/* CRLF seen; swallow both. */
newline++;
}
newline++;
break;
}
}
/* Ensure room in the net_buffer and append some or all of the current
chunk of data to it. */
{
const char *end = (newline ? newline : net_buffer_end);
uint32 desired_size = (end - net_buffer) + (*buffer_fpP) + 1;
if (desired_size >= (*buffer_sizeP))
{
status = XP_GrowBuffer (desired_size, sizeof(char), 1024,
bufferP, buffer_sizeP);
if (status < 0) return status;
}
XP_MEMCPY ((*bufferP) + (*buffer_fpP), net_buffer, (end - net_buffer));
(*buffer_fpP) += (end - net_buffer);
}
/* Now *bufferP contains either a complete line, or as complete
a line as we have read so far.
If we have a line, process it, and then remove it from `*bufferP'.
Then go around the loop again, until we drain the incoming data.
*/
if (!newline)
return 0;
status = xp_convert_and_send_buffer(*bufferP, *buffer_fpP,
convert_newlines_p,
per_line_fn, closure);
if (status < 0) return status;
net_buffer_size -= (newline - net_buffer);
net_buffer = newline;
(*buffer_fpP) = 0;
}
return 0;
}
/* The opposite of xp_LineBuffer(): takes small buffers and packs them
up into bigger buffers before passing them along.
Pass in a desired_buffer_size 0 to tell it to flush (for example, in
in the very last call to this function.)
*/
int
XP_ReBuffer (const char *net_buffer, int32 net_buffer_size,
uint32 desired_buffer_size,
char **bufferP, uint32 *buffer_sizeP, uint32 *buffer_fpP,
int32 (*per_buffer_fn) (char *buffer, uint32 buffer_size,
void *closure),
void *closure)
{
int status = 0;
if (desired_buffer_size >= (*buffer_sizeP))
{
status = XP_GrowBuffer (desired_buffer_size, sizeof(char), 1024,
bufferP, buffer_sizeP);
if (status < 0) return status;
}
do
{
int32 size = *buffer_sizeP - *buffer_fpP;
if (size > net_buffer_size)
size = net_buffer_size;
if (size > 0)
{
XP_MEMCPY ((*bufferP) + (*buffer_fpP), net_buffer, size);
(*buffer_fpP) += size;
net_buffer += size;
net_buffer_size -= size;
}
if (*buffer_fpP > 0 &&
*buffer_fpP >= desired_buffer_size)
{
status = (*per_buffer_fn) ((*bufferP), (*buffer_fpP), closure);
*buffer_fpP = 0;
if (status < 0) return status;
}
}
while (net_buffer_size > 0);
return 0;
}