/* Stream handling functions.

   Copyright (C) 1993 Sebastiano Vigna

    This file is part of ne, the nice editor.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */



#include "ne.h"

/* This is the least increment with which a char_stream is reallocated. */

#define CHAR_STREAM_SIZE_INC (2048)


/* This function allocates a stream of size bytes. Note that a size of 0 is
legal, in which case a char_stream structure is allocated, but its stream
pointer is left NULL. */

char_stream *alloc_char_stream(int size) {

	char_stream *cs;

	if (cs = calloc(1, sizeof(char_stream))) {
		if (!size || (cs->stream = calloc(1, size))) {
			cs->size = size;

			assert_char_stream(cs);

			return(cs);
		}
		free(cs);
	}
	return(NULL);
}

/* This function frees a stream. */

void free_char_stream(char_stream *cs) {

	if (!cs) return;

	assert_char_stream(cs);

	free(cs->stream);
	free(cs);
}


/* This function reallocates a stream. If cs is NULL, it is equivalent to
alloc_char_stream. Otherwise, the memory pointed by stream is realloc()ated
to size bytes. If the reallocation is successfull, cs is returned, otherwise
NULL. */

char_stream *realloc_char_stream(char_stream *cs, int size) {

	unsigned char *new_stream;

	if (!cs) return(alloc_char_stream(size));

	assert_char_stream(cs);

	if (new_stream = realloc(cs->stream, size)) {
		cs->size = size;
		cs->stream = new_stream;
		return(cs);
	}
	return(NULL);
}



/* This function concatenates a block of len bytes pointed to by s to a
stream. The stream is extended if necessary. */

int add_to_stream(char_stream *cs, const unsigned char *s, int len) {

	if (!s) return(OK);

	if (!cs) return(ERROR);

	if (cs->size-cs->len < len)
		if (!realloc_char_stream(cs, cs->size+len+CHAR_STREAM_SIZE_INC)) return(OUT_OF_MEMORY);

	memcpy(cs->stream+cs->len, s, len);
	cs->len += len;

	return(OK);
}



/* This function reset a character stream. If cs is NULL, an empty character
stream is returned. If it is non-null, everything inside it is freed. The
stream memory is deallocated, unless its size is smaller or equal to
2*CHAR_STREAM_SIZE_INC (so that we won't continously allocate and deallocate
small streams). */

char_stream *reset_stream(char_stream *cs) {

	if (!cs) return(alloc_char_stream(0));

	assert_char_stream(cs);

	cs->len = 0;

	if (cs->size > 2*CHAR_STREAM_SIZE_INC) {

		cs->size = 0;

		free(cs->stream);
		cs->stream = NULL;
	}

	return(cs);
}


/* This function returns TRUE is the stream is composed by just one line. */

int is_one_line(char_stream *cs) {

	assert_char_stream(cs);

	if (cs->stream && strlen(cs->stream)+1 < cs->len) return(FALSE);
	
	return(TRUE);

}


/* These two functions load a stream in memory. Carriage returns and line feeds
are converted to NULLs. You can pass NULL for cs, and a char stream will be
allocated for you. */

char_stream *load_stream(char_stream *cs, const unsigned char *name) {

	int fh;

	assert_char_stream(cs);

	assert(name != NULL);

	cs = load_stream_from_fh(cs, fh = open(tilde_expand(name), 0));
	if (fh>=0) close(fh);

	return(cs);
}

char_stream *load_stream_from_fh(char_stream *cs, int fh) {

	int i, len;

	if (fh<0) return(NULL);

	assert_char_stream(cs);

	len = lseek(fh, 0, 2);

	if (len<0) return(NULL);

	lseek(fh, 0, 0);

	if (!(cs = realloc_char_stream(cs, len+1))) return(NULL);

	if (read(fh, cs->stream, len)<len) {
		free_char_stream(cs);
		return(NULL);
	}

	for(i=0; i<len; i++)
		if (cs->stream[i] == 0x0A || cs->stream[i] == 0x0D) cs->stream[i] = 0;

	cs->stream[len] = 0;
	cs->len = len+1;

	assert_char_stream(cs);

	return(cs);
}



/* These two functions save a stream to file. NULLs are converted to line
feeds. */

int save_stream(char_stream *cs, const unsigned char *name) {

	int fh, error;

	if (!cs) return(-1);

	assert_char_stream(cs);

	assert(name != NULL);

	if ((fh = open(tilde_expand(name), 0x302, 0600))>=0) {
		error = save_stream_to_fh(cs, fh);
		close(fh);
		return(error);
	}

	return(CANT_OPEN_FILE);
}


int save_stream_to_fh(char_stream *cs, int fh) {

	int len;
	unsigned char *p;

	if (!cs) return(-1);

	assert_char_stream(cs);

	p = cs->stream;

	while(p-cs->stream < cs->len) {
		len = strlen(p);

		if (write(fh, p, len) < len || write(fh, "\n", 1) < 1) return(ERROR_WHILE_WRITING);

		p += len+1;
	}

	return(0);
}

